Risposte:
Prova questo :
In Objective C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
In Swift
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
stato sempre nil
così l'ho cambiato windows[0]
e rimosso ?
dal concatenamento opzionale, quindi ha funzionato.
Per ottenere l'altezza tra le guide di layout è sufficiente
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Quindi full frame height = 812.0
,safe area height = 734.0
Di seguito è riportato l'esempio in cui la vista verde ha una cornice di guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, che ha il frame sicuro.
Swift 4, 5
Per bloccare una vista su un ancoraggio di area sicura, è possibile utilizzare i vincoli in qualsiasi punto del ciclo di vita del controller della vista perché sono accodati dall'API e gestiti dopo che la vista è stata caricata in memoria. Tuttavia, ottenere valori di aree sicure richiede l'attesa verso la fine del ciclo di vita di un controller di visualizzazione, ad esempio viewDidLayoutSubviews()
.
Questo si collega a qualsiasi controller di visualizzazione:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = view.safeAreaInsets.top
bottomSafeArea = view.safeAreaInsets.bottom
} else {
topSafeArea = topLayoutGuide.length
bottomSafeArea = bottomLayoutGuide.length
}
// safe area values are now available to use
}
Preferisco questo metodo per toglierlo dalla finestra (quando possibile) perché è come è stata progettata l'API e, soprattutto, i valori vengono aggiornati durante tutte le modifiche alla vista, come le modifiche all'orientamento del dispositivo.
Tuttavia, alcuni controller di visualizzazione personalizzati non possono utilizzare il metodo sopra (sospetto perché si trovano in viste contenitore temporanee). In tali casi, è possibile ottenere i valori dal controller della vista radice, che sarà sempre disponibile in qualsiasi punto del ciclo di vita del controller della vista corrente.
anyLifecycleMethod()
guard let root = UIApplication.shared.keyWindow?.rootViewController else {
return
}
let topSafeArea: CGFloat
let bottomSafeArea: CGFloat
if #available(iOS 11.0, *) {
topSafeArea = root.view.safeAreaInsets.top
bottomSafeArea = root.view.safeAreaInsets.bottom
} else {
topSafeArea = root.topLayoutGuide.length
bottomSafeArea = root.bottomLayoutGuide.length
}
// safe area values are now available to use
}
viewDidLayoutSubviews
(che può essere chiamato più volte), ecco una soluzione che funzionerà anche in viewDidLoad
: stackoverflow.com/a/53864017/7767664
Nessuna delle altre risposte qui ha funzionato per me, ma questo ha funzionato.
var topSafeAreaHeight: CGFloat = 0
var bottomSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
bottomSafeAreaHeight = window.frame.maxY - safeFrame.maxY
}
Tutte le risposte qui sono utili, grazie a tutti coloro che hanno offerto aiuto.
Tuttavia, poiché vedo che l'argomento relativo all'area di sicurezza è un po 'confuso, il che non sembra essere ben documentato.
Quindi mi riassumere qui come poltiglia possibile per rendere più facile da capire safeAreaInsets
, safeAreaLayoutGuide
e LayoutGuide
.
In iOS 7, Apple ha introdotto le proprietà topLayoutGuide
e , Ti hanno permesso di creare vincoli per impedire che i tuoi contenuti venissero nascosti dalle barre UIKit come lo stato, la navigazione o la barra delle schede Era possibile con queste guide di layout specificare i vincoli sui contenuti, evitandoli essere nascosto dagli elementi di navigazione superiore o inferiore (barre UIKit, barra di stato, barra di navigazione o barra delle schede ...).bottomLayoutGuide
UIViewController
Quindi, ad esempio, se vuoi creare un tableView inizia dalla schermata principale, hai fatto qualcosa del genere:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
In iOS 11 Apple ha deprecato queste proprietà sostituendole con una singola guida al layout dell'area sicura
Area sicura secondo Apple
Le aree sicure consentono di posizionare le viste all'interno della parte visibile dell'interfaccia generale. I controller di visualizzazione definiti da UIKit possono posizionare viste speciali in cima ai tuoi contenuti. Ad esempio, un controller di navigazione visualizza una barra di navigazione in cima al contenuto del controller di visualizzazione sottostante. Anche quando tali viste sono parzialmente trasparenti, occludono comunque il contenuto sottostante. In tvOS, l'area sicura include anche inserti overscan dello schermo, che rappresentano l'area coperta dalla cornice dello schermo.
Di seguito, un'area sicura evidenziata in iPhone 8 e iPhone serie X:
Il safeAreaLayoutGuide
è una struttura diUIView
Per ottenere l'altezza di safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
Ciò restituirà l'altezza della freccia nella tua foto.
Ora, che ne dici di ottenere le altezze di "tacca" e di fondo della schermata principale in alto?
Qui useremo il safeAreaInsets
L'area sicura di una vista riflette l'area non coperta da barre di navigazione, barre delle schede, barre degli strumenti e altri antenati che oscurano la vista di un controller di vista. (In tvOS, l'area sicura riflette l'area non coperta dalla cornice dello schermo.) Si ottiene l'area sicura per una vista applicando gli inserti in questa proprietà al rettangolo dei limiti della vista. Se la vista non è attualmente installata in una gerarchia di viste o non è ancora visibile sullo schermo, gli inserti dei bordi in questa proprietà sono 0.
Quanto segue mostrerà l'area non sicura e la distanza dai bordi su iPhone 8 e uno di iPhone X-Series.
Ora, se aggiunta la barra di navigazione
Quindi, ora come ottenere l'altezza dell'area non sicura? useremo ilsafeAreaInset
Ecco le soluzioni, tuttavia differiscono in una cosa importante,
Il primo:
self.view.safeAreaInsets
Ciò restituirà EdgeInsets, ora puoi accedere in alto e in basso per conoscere gli inserti,
Il secondo:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
Il primo a cui stai prendendo gli inserti di visualizzazione, quindi se viene considerata una barra di navigazione, verrà presa in considerazione la seconda, mentre stai accedendo a safeAreaInsets della finestra, quindi la barra di navigazione non verrà considerata
In iOS 11 c'è un metodo che dice quando è cambiato SafeArea.
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
let top = view.safeAreaInsets.top
let bottom = view.safeAreaInsets.bottom
}
safeAreaLayoutGuide Quando la vista è visibile sullo schermo, questa guida riflette la parte della vista che non è coperta da barre di navigazione, barre delle schede, barre degli strumenti e altre viste degli antenati. (In tvOS, l'area di sicurezza riflette l'area non coperta dalla cornice dello schermo.) Se la vista non è attualmente installata in una gerarchia di viste o non è ancora visibile sullo schermo, i bordi della guida del layout sono uguali ai bordi della vista.
Quindi per ottenere l'altezza della freccia rossa nello screenshot è:
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Darà un avviso di deprecazione. '' keyWindow 'è stato deprecato in iOS 13.0: non deve essere utilizzato per applicazioni che supportano più scene poiché restituisce una finestra chiave in tutte le scene connesse a causa delle scene connesse. Uso così.
extension UIView {
var safeAreaBottom: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.bottom
}
}
return 0
}
var safeAreaTop: CGFloat {
if #available(iOS 11, *) {
if let window = UIApplication.shared.keyWindowInConnectedScenes {
return window.safeAreaInsets.top
}
}
return 0
}
}
extension UIApplication {
var keyWindowInConnectedScenes: UIWindow? {
return windows.first(where: { $0.isKeyWindow })
}
}
Objective-C Chi ha avuto il problema quando keyWindow è uguale a zero . Basta inserire il codice sopra in viewDidAppear (non in viewDidLoad)
Estensione Swift 5
Può essere usato come estensione e chiamato con: UIApplication.topSafeAreaHeight
extension UIApplication {
static var topSafeAreaHeight: CGFloat {
var topSafeAreaHeight: CGFloat = 0
if #available(iOS 11.0, *) {
let window = UIApplication.shared.windows[0]
let safeFrame = window.safeAreaLayoutGuide.layoutFrame
topSafeAreaHeight = safeFrame.minY
}
return topSafeAreaHeight
}
}
L'estensione di UIApplication è facoltativa, può essere un'estensione di UIView o qualunque cosa sia preferita, o probabilmente anche meglio una funzione globale.
Un approccio più arrotondato
import SnapKit
let containerView = UIView()
containerView.backgroundColor = .red
self.view.addSubview(containerView)
containerView.snp.remakeConstraints { (make) -> Void in
make.width.top.equalToSuperView()
make.top.equalTo(self.view.safeArea.top)
make.bottom.equalTo(self.view.safeArea.bottom)
}
extension UIView {
var safeArea: ConstraintBasicAttributesDSL {
if #available(iOS 11.0, *) {
return self.safeAreaLayoutGuide.snp
}
return self.snp
}
var isIphoneX: Bool {
if #available(iOS 11.0, *) {
if topSafeAreaInset > CGFloat(0) {
return true
} else {
return false
}
} else {
return false
}
}
var topSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var topPadding: CGFloat = 0
if #available(iOS 11.0, *) {
topPadding = window?.safeAreaInsets.top ?? 0
}
return topPadding
}
var bottomSafeAreaInset: CGFloat {
let window = UIApplication.shared.keyWindow
var bottomPadding: CGFloat = 0
if #available(iOS 11.0, *) {
bottomPadding = window?.safeAreaInsets.bottom ?? 0
}
return bottomPadding
}
}
Per quelli di voi che cambiano in modalità orizzontale, è necessario assicurarsi di utilizzare viewSafeAreaInsetsDidChange dopo la rotazione per ottenere i valori più aggiornati:
private var safeAreaInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
override func viewSafeAreaInsetsDidChange() {
if #available(iOS 11.0, *) {
safeAreaInsets = UIApplication.shared.keyWindow!.safeAreaInsets
}
}