Respuestas:
Prueba esto :
En el objetivo C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
En veloz
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
siempre fue nil
así que lo cambié windows[0]
y lo eliminé del ?
encadenamiento opcional, luego funcionó.
Para obtener la altura entre las guías de diseño, simplemente haz
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Por lo tanto full frame height = 812.0
,safe area height = 734.0
A continuación se muestra el ejemplo donde la vista verde tiene un marco de guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, que tiene el marco seguro.
Swift 4, 5
Para anclar una vista a un ancla de área segura, se pueden usar restricciones en cualquier parte del ciclo de vida del controlador de vista porque la API las pone en cola y las maneja después de que la vista se haya cargado en la memoria. Sin embargo, obtener valores de área segura requiere esperar hacia el final del ciclo de vida de un controlador de vista, como viewDidLayoutSubviews()
.
Esto se conecta a cualquier controlador de vista:
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
}
Prefiero este método a sacarlo de la ventana (cuando sea posible) porque es cómo se diseñó la API y, lo que es más importante, los valores se actualizan durante todos los cambios de vista, como los cambios de orientación del dispositivo.
Sin embargo, algunos controladores de vista personalizados no pueden usar el método anterior (sospecho que están en vistas de contenedor transitorio). En tales casos, puede obtener los valores del controlador de vista raíz, que siempre estará disponible en cualquier parte del ciclo de vida del controlador de vista actual.
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
(que se puede llamar varias veces), aquí hay una solución que funcionará incluso en viewDidLoad
: stackoverflow.com/a/53864017/7767664
Ninguna de las otras respuestas aquí funcionó para mí, pero esto sí.
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
}
Todas las respuestas aquí son útiles, gracias a todos los que ofrecieron ayuda.
Sin embargo, como veo que el tema del área segura está un poco confundido, lo que no parece estar bien documentado.
Así que lo resumiré aquí lo más rápido posible para que sea fácil de entender safeAreaInsets
, safeAreaLayoutGuide
yLayoutGuide
.
En iOS 7, Apple introdujo las propiedades topLayoutGuide
y bottomLayoutGuide
enUIViewController
, le permitieron crear restricciones para evitar que su contenido se ocultara en barras UIKit como el estado, la navegación o la barra de pestañas. Con estas guías de diseño era posible especificar restricciones en el contenido, evitándolo estar oculto por los elementos de navegación superior o inferior (barras UIKit, barra de estado, barra de navegación o pestaña ...).
Entonces, por ejemplo, si quieres hacer que una vista de tabla comience desde la pantalla superior, has hecho algo así:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
En iOS 11, Apple ha desaprobado estas propiedades reemplazándolas con una sola guía de diseño de área segura
Área segura según Apple
Las áreas seguras lo ayudan a colocar sus vistas dentro de la parte visible de la interfaz general. Los controladores de vista definidos por UIKit pueden colocar vistas especiales en la parte superior de su contenido. Por ejemplo, un controlador de navegación muestra una barra de navegación en la parte superior del contenido del controlador de vista subyacente. Incluso cuando tales vistas son parcialmente transparentes, aún ocluyen el contenido que está debajo de ellas. En tvOS, el área segura también incluye las inserciones de sobreexploración de la pantalla, que representan el área cubierta por el bisel de la pantalla.
A continuación, un área segura resaltada en iPhone 8 y iPhone X-series:
El safeAreaLayoutGuide
es una propiedad deUIView
Para obtener la altura de safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
Eso devolverá la altura de la flecha en su imagen.
Ahora, ¿qué pasa con la obtención de las alturas de los indicadores de "muesca" superior e inferior de la pantalla de inicio?
Aquí usaremos el safeAreaInsets
El área segura de una vista refleja el área no cubierta por barras de navegación, barras de pestañas, barras de herramientas y otros antepasados que oscurecen la vista de un controlador de vista. (En tvOS, el área segura refleja el área no cubierta por el bisel de la pantalla). Usted obtiene el área segura para una vista aplicando las inserciones de esta propiedad al rectángulo de límites de la vista. Si la vista no está instalada actualmente en una jerarquía de vistas, o aún no está visible en la pantalla, las inserciones de borde en esta propiedad son 0.
A continuación se mostrará el área insegura y la distancia desde los bordes en el iPhone 8 y uno de la serie X del iPhone.
Ahora, si se agrega la barra de navegación
Entonces, ¿cómo obtener la altura del área insegura? usaremos elsafeAreaInset
Aquí hay soluciones, sin embargo, difieren en algo importante,
El primero:
self.view.safeAreaInsets
Eso devolverá los EdgeInsets, ahora puede acceder a la parte superior e inferior para conocer las inserciones,
Segundo:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
El primero está tomando los recuadros de vista, por lo que si hay una barra de navegación se considerará, sin embargo, el segundo está accediendo a safeAreaInsets de la ventana, por lo que la barra de navegación no se considerará
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Dará una advertencia de desaprobación. 'keyWindow' quedó en desuso en iOS 13.0: no debe usarse para aplicaciones que admitan varias escenas, ya que devuelve una ventana clave en todas las escenas conectadas 'debido a las escenas conectadas. Yo uso de esta manera.
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 })
}
}
safeAreaLayoutGuide Cuando la vista es visible en pantalla, esta guía refleja la parte de la vista que no está cubierta por barras de navegación, barras de pestañas, barras de herramientas y otras vistas de antepasados. (En tvOS, el área segura refleja el área que no cubre el bisel de la pantalla). Si la vista no está instalada actualmente en una jerarquía de vistas o aún no está visible en la pantalla, los bordes de la guía de diseño son iguales a los bordes de la vista.
Luego, para obtener la altura de la flecha roja en la captura de pantalla, es:
self.safeAreaLayoutGuide.layoutFrame.size.height
Objective-C ¿Quién tuvo el problema cuando keyWindow es igual a nil ? Simplemente ponga el código de arriba en viewDidAppear (no en viewDidLoad)
Extensión Swift 5
Esto se puede usar como una extensión y llamar 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
}
}
La extensión de la aplicación UIA es opcional, puede ser una extensión de UIView o lo que sea preferido, o probablemente incluso una función global.
Un enfoque más redondeado
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
}
}
Para aquellos de ustedes que cambian al modo horizontal, deben asegurarse de usar viewSafeAreaInsetsDidChange después de la rotación para obtener los valores más actualizados:
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
}
}