获取安全区域插图的顶部和底部高度


Answers:


365

试试这个 :

目标C中

if (@available(iOS 11.0, *)) {
    UIWindow *window = UIApplication.sharedApplication.keyWindow;
    CGFloat topPadding = window.safeAreaInsets.top;
    CGFloat bottomPadding = window.safeAreaInsets.bottom;
}

斯威夫特

if #available(iOS 11.0, *) {
    let window = UIApplication.shared.keyWindow
    let topPadding = window?.safeAreaInsets.top
    let bottomPadding = window?.safeAreaInsets.bottom
}

1
@KrutikaSonawala bottomPadding + 10(您的价值)
user6788419

12
这似乎对我不起作用-打印出UIApplication.shared.keyWindow?.safeAreaInsets的值将返回全0。有任何想法吗?

2
我做到了,我可以限制任何视图的safeAreaLayoutGuides,但是直接打印出关键窗口的safeAreaInsets的值只会返回零矩形。这是有问题的,因为我需要手动约束已添加到关键窗口但不在根视图控制器的视图层次结构中的视图。

6
对我而言,keyWindow总是nil如此,因此我将其更改为可选链windows[0]并将其?从可选链中删除,然后它起作用了。
George_E

2
它仅在控制器的视图已经出现时才起作用。
Vanya

84

要获得布局指南之间的高度,您只需执行

let guide = view.safeAreaLayoutGuide
let height = guide.layoutFrame.size.height

所以full frame height = 812.0safe area height = 734.0

下面是绿色视图的框架为 guide.layoutFrame

在此处输入图片说明


谢谢那是我也在考虑的线索。我认为没有更可靠的解决方案。但是这样我只能得到不安全的高度,对吗?每个区域不是两个高度吗?
Tulleb

2
摆好视图后,这将为您提供正确的答案
Ladislav

2
其实@ user6788419的答案看起来也不错而且更短。
Tulleb

使用控制器的视图,该代码的高度为812.0。所以我正在使用UIApplication.sharedApplication.keyWindow.safeAreaLayoutGuide.layoutFrame,它确实具有安全框架。
Ferran Maylinch '18

1
@FerranMaylinch不知道在哪里检查高度,但是我也有812,原来是由于在查看视图之前检查了值。在那种情况下,高度将是相同的“如果视图当前未安装在视图层次结构中,或者在屏幕上尚不可见,则布局参考线的边缘将等于视图的边缘” 。developer.apple.com/documentation/ uikit / uiview /…
Nicholas Harlen '18

81

斯威夫特4,5

使用约束将视图固定到安全区域锚可以在视图控制器生命周期的任何地方完成,因为约束是由API排队并在视图加载到内存后进行处理的。但是,获取安全区域值需要等到视图控制器生命周期的尽头,例如viewDidLayoutSubviews()

这可以插入任何视图控制器:

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
}

我更喜欢这种方法(如果可能的话)使其脱离窗口,因为这是API的设计方式,更重要的是,在所有视图更改(例如设备方向更改)期间都会更新值。

但是,某些自定义呈现的视图控制器无法使用上述方法(我怀疑是因为它们处于瞬时容器视图中)。在这种情况下,您可以从根视图控制器获取值,该值将始终在当前视图控制器生命周期的任何位置可用。

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
}

3
请声明使用let topSafeArea:CGFloat代替!
Gusutafu

避免使用viewDidLayoutSubviews(可以多次调用),这是一个即使在viewDidLoad以下情况下也可以使用的解决方案:stackoverflow.com/a/53864017/7767664
user924

@ user924,那么当安全区域更改(即双高状态栏)时,值将不会更新
bsod

@bsod,那么这会更好stackoverflow.com/a/3420550/7767664(因为它仅用于状态栏而不是其他视图)
user924

或者,您不必使用应用程序委托,而可以使用视图控制器中内置的Apple安全区域API。
bsod

21

这里没有其他答案对我有用,但是确实可以。

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
  }

1
您的代码将在视图控制器生命周期的任何部分中工作。如果要使用view.safeAreaInsets.top,则需要确保在viewDidAppear或更高版本中调用它。在viewDidLoad中,您将无法获得具有正确值的view.safeAreaInsets.top,因为它尚未初始化。
user3204765'4

1
迄今为止最好的答案
Rikco

17

感谢所有提供帮助的人,这里的所有答案都很有帮助。

但是,正如我所看到的,安全区域主题有些混乱,似乎没有得到很好的记录。

因此,在这里我将尽可能概括它,以使其易于理解safeAreaInsetssafeAreaLayoutGuide并且LayoutGuide

在iOS 7中,Apple在中引入了topLayoutGuidebottomLayoutGuide属性UIViewController,它们允许您创建约束以防止内容被状态,导航或选项卡之类的UIKit栏隐藏。使用这些布局指南可以指定内容约束,避免出现约束被顶部或底部导航元素(UIKit栏,状态栏,导航或标签栏…)隐藏。

因此,例如,如果您想使tableView从顶部屏幕开始,您将执行以下操作:

self.tableView.contentInset = UIEdgeInsets(top: -self.topLayoutGuide.length, left: 0, bottom: 0, right: 0)

在iOS 11中,Apple已弃用这些属性,并以一个安全区域布局指南代替了它们

根据Apple的安全区域

安全区域可帮助您将视图放置在整个界面的可见部分内。UIKit定义的视图控制器可以在内容顶部放置特殊视图。例如,导航控制器在基础视图控制器内容的顶部显示导航栏。即使这些视图是部分透明的,它们仍然会遮挡其下面的内容。在tvOS中,安全区域还包括屏幕的过扫描插图,这些插图代表屏幕边框所覆盖的区域。

下面是iPhone 8和iPhone X系列中突出显示的安全区域:

在此处输入图片说明

safeAreaLayoutGuide是一个属性UIView

达到以下高度safeAreaLayoutGuide

extension UIView {
   var safeAreaHeight: CGFloat {
       if #available(iOS 11, *) {
        return safeAreaLayoutGuide.layoutFrame.size.height
       }
       return bounds.height
  }
}

这将返回图片中箭头的高度。

现在,如何获得顶部的“缺口”和底部的主屏幕指示器高度?

在这里我们将使用 safeAreaInsets

视图的安全区域反映了导航栏,选项卡栏,工具栏和其他使视图控制器的视图模糊的祖先所没有覆盖的区域。(在tvOS中,安全区域反映了屏幕边框未覆盖的区域。)通过将此属性中的插图应用于视图的边界矩形,可以获取视图的安全区域。如果该视图当前未安装在视图层次结构中,或者在屏幕上尚不可见,则此属性的边缘插图为0。

以下内容将显示iPhone 8和iPhone X系列之一上的不安全区域以及与边缘的距离。

在此处输入图片说明

在此处输入图片说明

现在,如果添加了导航栏

在此处输入图片说明

在此处输入图片说明

那么,现在如何获得不安全区域的高度?我们将使用safeAreaInset

这里是解决方案,但是它们在重要方面有所不同,

第一:

self.view.safeAreaInsets

这将返回EdgeInsets,您现在可以访问顶部和底部以了解插图,

第二个:

UIApplication.shared.windows.first{$0.isKeyWindow }?.safeAreaInsets

您第一个正在获取视图插图,因此,如果有导航栏,它将被考虑,但是第二个您正在访问窗口的safeAreaInsets,因此将不考虑导航栏


5

在iOS 11中,有一种方法可以告知safeArea何时更改。

override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()
    let top = view.safeAreaInsets.top
    let bottom = view.safeAreaInsets.bottom
}

2

我用的CocoaPods框架和工作的情况下UIApplication.shared不可用的,然后我用safeAreaInsets在视图的window

if #available(iOS 11.0, *) {
    let insets = view.window?.safeAreaInsets
    let top = insets.top
    let bottom = insets.bottom
}

2

safeAreaLayoutGuide 当视图在屏幕上可见时,本指南将反映该视图的未被导航栏,标签栏,工具栏和其他祖先视图覆盖的部分。(在tvOS中,安全区域反映的是未被屏幕边框覆盖的区域。)如果视图当前未安装在视图层次结构中或在屏幕上尚不可见,则布局参考线的边缘将等于视图的边缘。

然后要获得屏幕截图中红色箭头的高度,它是:

self.safeAreaLayoutGuide.layoutFrame.size.height

2

Swift 5,Xcode 11.4

`UIApplication.shared.keyWindow` 

它将发出弃用警告。在iOS 13.0中已弃用了“ keyWindow”:不应用于支持多个场景的应用程序,因为它会由于连接的场景而在所有连接的场景之间返回一个关键窗口。我用这种方式。

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 })
    }
}

1

Objective-C keyWindow等于nil 时谁遇到了问题。只需将上面的代码放在viewDidAppear中(不在viewDidLoad中)


1
UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; 
CGFloat fBottomPadding = window.safeAreaInsets.bottom;

1

Swift 5扩展

可以用作扩展,并可以通过以下方式调用: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
    }
}

UIApplication的扩展是可选的,可以是UIView的扩展或任何首选的扩展,甚至可以是更好的全局函数。


0

更全面的方法

  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
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.