Respostas:
Tente o seguinte:
No objetivo C
if (@available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat topPadding = window.safeAreaInsets.top;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
}
Na Swift
if #available(iOS 11.0, *) {
let window = UIApplication.shared.keyWindow
let topPadding = window?.safeAreaInsets.top
let bottomPadding = window?.safeAreaInsets.bottom
}
keyWindow
sempre foi nil
assim que eu mudei windows[0]
e removi o ?
encadeamento opcional e funcionou.
Para obter a altura entre as guias de layout, basta
let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height
Assim full frame height = 812.0
,safe area height = 734.0
Abaixo está o exemplo em que a vista verde possui um quadro de guide.layoutFrame
UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame
, que tem o quadro seguro.
Swift 4, 5
Para fixar uma exibição em uma âncora de área segura, o uso de restrições pode ser feito em qualquer lugar do ciclo de vida do controlador de exibição, porque eles são enfileirados pela API e manipulados após o carregamento da exibição na memória. No entanto, para obter valores de área segura, é necessário aguardar o final do ciclo de vida de um controlador de exibição, comoviewDidLayoutSubviews()
.
Isso se conecta a qualquer controlador de exibição:
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
}
Prefiro esse método a tirá-lo da janela (quando possível) porque é como a API foi projetada e, mais importante, os valores são atualizados durante todas as alterações de exibição, como alterações na orientação do dispositivo.
No entanto, alguns controladores de exibição apresentados personalizados não podem usar o método acima (suspeito porque estão em visualizações de contêiner transitório). Nesses casos, é possível obter os valores do controlador de exibição raiz, que sempre estará disponível em qualquer lugar do ciclo de vida do controlador de exibição atual.
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 pode ser chamado várias vezes), aqui está uma solução que funcionará mesmo em viewDidLoad
: stackoverflow.com/a/53864017/7767664
Nenhuma das outras respostas aqui funcionou para mim, mas isso funcionou.
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 as respostas aqui são úteis. Obrigado a todos que ofereceram ajuda.
No entanto, como vejo que o tópico da área de segurança está um pouco confuso, o que não parece estar bem documentado.
Então eu vou resumi-lo aqui como mush possível para torná-lo fácil de compreender safeAreaInsets
, safeAreaLayoutGuide
e LayoutGuide
.
No iOS 7, a Apple introduziu as propriedades topLayoutGuide
e , Eles permitiram criar restrições para impedir que seu conteúdo fosse oculto pelas barras do UIKit, como status, navegação ou barra de guias. oculto pelos elementos de navegação superior ou inferior (barras do UIKit, barra de status, barra de navegação ou tabulação…).bottomLayoutGuide
UIViewController
Então, por exemplo, se você deseja criar um tableView a partir da tela superior, você fez algo assim:
self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)
No iOS 11, a Apple substituiu essas propriedades, substituindo-as por um único guia de layout de área segura
Área segura de acordo com a Apple
As áreas seguras ajudam a colocar suas visualizações na parte visível da interface geral. Os controladores de exibição definidos pelo UIKit podem posicionar visualizações especiais sobre o seu conteúdo. Por exemplo, um controlador de navegação exibe uma barra de navegação na parte superior do conteúdo do controlador de exibição subjacente. Mesmo quando essas visualizações são parcialmente transparentes, elas ainda ocultam o conteúdo que está abaixo delas. No tvOS, a área segura também inclui as inserções de overscan da tela, que representam a área coberta pelo painel da tela.
Abaixo, uma área segura destacada no iPhone 8 e iPhone X-series:
O safeAreaLayoutGuide
é uma propriedade deUIView
Para obter a altura de safeAreaLayoutGuide
:
extension UIView {
var safeAreaHeight: CGFloat {
if #available(iOS 11, *) {
return safeAreaLayoutGuide.layoutFrame.size.height
}
return bounds.height
}
}
Isso retornará a altura da seta na sua foto.
Agora, que tal obter as alturas dos indicadores superiores e inferiores da tela inicial?
Aqui vamos usar o safeAreaInsets
A área segura de uma exibição reflete a área não coberta por barras de navegação, guias, barras de ferramentas e outros ancestrais que obscurecem a exibição de um controlador de exibição. (No tvOS, a área segura reflete a área não coberta pelo painel da tela.) Você obtém a área segura para uma vista aplicando as inserções nessa propriedade ao retângulo de limites da vista. Se a visualização não estiver instalada no momento em uma hierarquia de visualização ou ainda não estiver visível na tela, as inserções de borda nesta propriedade serão 0.
A seguir, será mostrada a área insegura e a distância das bordas do iPhone 8 e de um do iPhone X-Series.
Agora, se a barra de navegação tiver sido adicionada
Então, agora como obter a altura da área insegura? vamos usar osafeAreaInset
Aqui estão as soluções, porém elas diferem em algo importante,
Primeiro:
self.view.safeAreaInsets
Isso retornará os EdgeInsets. Agora você pode acessar a parte superior e a parte inferior para conhecer as inserções,
O segundo:
UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets
O primeiro que você está visualizando, portanto, se houver uma barra de navegação, ela será considerada; no entanto, o segundo, você estará acessando o safeAreaInsets da janela, para que a barra de navegação não seja considerada
safeAreaLayoutGuide Quando a exibição é visível na tela, este guia reflete a parte da exibição que não é coberta por barras de navegação, guias, barras de ferramentas e outras exibições ancestrais. (No tvOS, a área segura reflete a área não coberta pelo painel da tela.) Se a visualização não estiver atualmente instalada em uma hierarquia de visualizações ou ainda não estiver visível na tela, as bordas da guia de layout serão iguais às bordas da visualização.
Então, para obter a altura da seta vermelha na captura de tela, é:
self.safeAreaLayoutGuide.layoutFrame.size.height
Swift 5, Xcode 11.4
`UIApplication.shared.keyWindow`
Isso dará um aviso de descontinuação. '' keyWindow 'foi descontinuado no iOS 13.0: não deve ser usado para aplicativos que suportam várias cenas, pois retorna uma janela de chave em todas as cenas conectadas' por causa das cenas conectadas. Eu uso assim.
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 })
}
}
Objetivo-C Quem teve o problema quando keyWindow é igual a zero . Basta colocar o código acima em viewDidAppear (não em viewDidLoad)
Extensão Swift 5
Isso pode ser usado como uma extensão e chamado com: 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
}
}
A extensão do UIApplication é opcional, pode ser uma extensão do UIView ou o que for preferido, ou provavelmente uma função global ainda melhor.
Uma abordagem mais arredondada
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 aqueles que mudam para o modo paisagem, use o viewSafeAreaInsetsDidChange após a rotação para obter os valores mais atualizados:
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
}
}