Réponses:
Essaye ça :
Dans l' objectif C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
Dans Swift
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
c'était toujours nil
donc je l'ai changé windows[0]
et retiré du ?
chaînage facultatif, puis cela a fonctionné.
Pour obtenir la hauteur entre les guides de mise en page, vous venez de faire
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Alors full frame height = 812.0
,safe area height = 734.0
Voici l'exemple où la vue verte a un cadre de guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, qui a le cadre sûr.
Swift 4, 5
L'épinglage d'une vue à une ancre de zone sûre à l'aide de contraintes peut être effectué n'importe où dans le cycle de vie du contrôleur de vue, car ils sont mis en file d'attente par l'API et gérés une fois la vue chargée en mémoire. Cependant, pour obtenir des valeurs de zone de sécurité, il faut attendre la fin du cycle de vie d'un contrôleur de vue, comme viewDidLayoutSubviews()
.
Cela se branche sur n'importe quel contrôleur de vue:
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
}
Je préfère cette méthode à la sortie de la fenêtre (si possible) car c'est ainsi que l'API a été conçue et, plus important encore, les valeurs sont mises à jour lors de tous les changements de vue, comme les changements d'orientation de l'appareil.
Cependant, certains contrôleurs de vue présentés personnalisés ne peuvent pas utiliser la méthode ci-dessus (je pense qu'ils sont dans des vues de conteneur transitoires). Dans de tels cas, vous pouvez obtenir les valeurs du contrôleur de vue racine, qui sera toujours disponible n'importe où dans le cycle de vie du contrôleur de vue actuel.
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
(qui peut être appelé plusieurs fois), voici une solution qui fonctionnera même dans viewDidLoad
: stackoverflow.com/a/53864017/7767664
Aucune des autres réponses ici n'a fonctionné pour moi, mais cela a fonctionné.
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
}
Toutes les réponses ici sont utiles, merci à tous ceux qui ont offert leur aide.
Cependant, comme je vois que le sujet de la zone de sécurité est un peu confus, ce qui ne semble pas être bien documenté.
Je vais donc le résumer ici le plus possible pour le rendre facile à comprendre safeAreaInsets
, safeAreaLayoutGuide
et LayoutGuide
.
Dans iOS 7, Apple a introduit les propriétés topLayoutGuide
et bottomLayoutGuide
dans UIViewController
, Ils vous ont permis de créer des contraintes pour empêcher votre contenu d'être masqué par les barres UIKit comme l'état, la navigation ou la barre d'onglets Il était possible avec ces guides de mise en page de spécifier des contraintes sur le contenu, en l'évitant à masquer par les éléments de navigation du haut ou du bas (barres UIKit, barre d'état, barre de navigation ou d'onglets…).
Donc, par exemple, si vous voulez faire une tableView commence à partir de l'écran supérieur, vous avez fait quelque chose comme ça:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
Dans iOS 11, Apple a désapprouvé ces propriétés en les remplaçant par un seul guide de disposition de zone de sécurité
Zone sûre selon Apple
Les zones sûres vous aident à placer vos vues dans la partie visible de l'interface globale. Les contrôleurs de vue définis par UIKit peuvent positionner des vues spéciales au-dessus de votre contenu. Par exemple, un contrôleur de navigation affiche une barre de navigation au-dessus du contenu du contrôleur de vue sous-jacent. Même lorsque ces vues sont partiellement transparentes, elles masquent toujours le contenu qui se trouve en dessous. Dans tvOS, la zone de sécurité comprend également les encarts de surbalayage de l'écran, qui représentent la zone couverte par le cadre de l'écran.
Ci-dessous, une zone de sécurité mise en évidence dans les séries iPhone 8 et iPhone X:
Le safeAreaLayoutGuide
est une propriété deUIView
Pour obtenir la hauteur de safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
Cela renverra la hauteur de la flèche dans votre image.
Maintenant, que diriez-vous d'obtenir les hauteurs d'indicateur «cran» supérieur et inférieur de l'écran d'accueil?
Ici, nous utiliserons le safeAreaInsets
La zone de sécurité d'une vue reflète la zone non couverte par les barres de navigation, les barres d'onglets, les barres d'outils et autres ancêtres qui obscurcissent la vue d'un contrôleur de vue. (Dans tvOS, la zone de sécurité reflète la zone non couverte par le cadre de l'écran.) Vous obtenez la zone de sécurité pour une vue en appliquant les encarts de cette propriété au rectangle des limites de la vue. Si la vue n'est pas actuellement installée dans une hiérarchie de vues ou n'est pas encore visible à l'écran, les encarts d'arête dans cette propriété sont 0.
Ce qui suit montre la zone dangereuse et la distance des bords sur l'iPhone 8 et l'un des iPhone X-Series.
Maintenant, si la barre de navigation est ajoutée
Alors, maintenant comment obtenir la hauteur de la zone dangereuse? nous utiliserons lesafeAreaInset
Voici les solutions mais elles diffèrent par une chose importante,
Premier:
self.view.safeAreaInsets
Cela renverra les EdgeInsets, vous pouvez maintenant accéder au haut et au bas pour connaître les inserts,
Deuxième:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
Le premier, vous prenez les inserts de vue, donc s'il y a une barre de navigation, cela sera pris en compte, mais le second vous accédez aux safeAreaInsets de la fenêtre afin que la barre de navigation ne soit pas prise en compte
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Cela donnera un avertissement de désapprobation. «keyWindow» était obsolète dans iOS 13.0: ne doit pas être utilisé pour les applications qui prennent en charge plusieurs scènes car il renvoie une fenêtre clé sur toutes les scènes connectées »en raison des scènes connectées. J'utilise de cette façon.
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 Lorsque la vue est visible à l'écran, ce guide reflète la partie de la vue qui n'est pas couverte par les barres de navigation, les barres d'onglets, les barres d'outils et autres vues des ancêtres. (Dans tvOS, la zone de sécurité reflète la zone non couverte par le cadre de l'écran.) Si la vue n'est pas actuellement installée dans une hiérarchie de vues, ou n'est pas encore visible à l'écran, les bords du guide de disposition sont égaux aux bords de la vue.
Ensuite, pour obtenir la hauteur de la flèche rouge dans la capture d'écran, c'est:
self.safeAreaLayoutGuide.layoutFrame.size.height
Objective-C Qui a eu le problème lorsque keyWindow est égal à nil . Il suffit de mettre le code ci-dessus dans viewDidAppear (pas dans viewDidLoad)
Extension Swift 5
Cela peut être utilisé comme une extension et appelé avec: 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'extension de UIApplication est facultative, peut être une extension de UIView ou tout ce qui est préféré, ou probablement encore mieux une fonction globale.
Une approche plus arrondie
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
}
}
Pour ceux d'entre vous qui passent en mode paysage, vous devez vous assurer d'utiliser viewSafeAreaInsetsDidChange après la rotation pour obtenir les valeurs les plus à jour:
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
}
}