在iPhone X上,UIView中的safeAreaInsets为0


72

我正在更新我的应用程序以使其适合iPhoneX。除一个视图外,到目前为止,所有视图都可以正常工作。我有一个视图控制器,它提供了一个覆盖整个屏幕的自定义UIView。在UIScreen.main.bounds完成所有布局之前,我一直未找到视图的大小(我需要使用它来为collectionView放置正确的itemSize)。我以为现在我可以做一些事情 UIScreen.main.bounds.size.height - safeAreaInsets.bottom来获得合适的可用尺寸。问题是,safeAreaInsets尝试在iPhone X(模拟器)上返回(0,0,0,0)。有任何想法吗?在其他视图中,我得到了safeAreaInsets的正确数字。

谢谢!


4
无论何时何地,你可以使用:'如果#available(iOS的11.0,tvOS 11.0,*)返回FALSE` {回报UIApplication.shared.delegate .window ?? safeAreaInsets = .zero?!} SOURCE
心教堂

根据下面的火星答案,我还看到获取这些的唯一可靠方法是在viewDidLayoutSubviews()或viewDidAppear中。实际上,“重写func viewSafeAreaInsetsDidChange()”似乎效果最好。
David H

Answers:


83

最近,我遇到了类似的问题,即一旦触发viewDidLoad,安全区域插图就会返回(0,0,0,0)。似乎它们的设置比视图加载的其余部分晚。

我通过重写viewSafeAreaInsetsDidChange并在其中进行布局来解决这个问题:

override func viewSafeAreaInsetsDidChange() {
 // ... your layout code here
} 

1
同上-这是正确的-尽管它是一个有点哑是这样的话
乔纳森的Plackett

2
如果OP希望在自定义视图中而不是在视图控制器中执行此操作,则还有UIView方法safeAreaInsetsDidChange
阿达(Arda)

1
如果您要在
视图控制器中

1
我已更改为横向模式,这是唯一对我有用的答案!
Oz Shabat

1
奇迹般有效。不知道我怎么能在过去的5年中幸存下来,却不知道这种方法:/
Trev14

42

我已经找到了解决方案:我正在init视图中进行所有实现。safeAreaInsets在中具有正确的大小layoutSubviews()


11
如果您使用viewUIViewController,你也可以做到这一点的viewDidLayoutSubviews(),它应该工作,因为它为我做的。
Natanel

1
请注意,在viewWillLayoutSubviews子视图上的saveAreaInsets值错误。
斯拉夫

您也可以在UIViewController.viewDidLayoutSubviews中执行此操作。
Christopher Schardt

37

我也遇到过这个问题,试图向上移动视图以为iPhone X上的键盘腾出空间。主视图的safeAreaInsets始终为0,即使我知道此时子视图已经作为屏幕布置了已被绘制。如上所述,我发现的一种解决方法是获取keyWindow并检查其安全区域插图。

对象:

CGFloat bottomInset = [UIApplication sharedApplication].keyWindow.safeAreaInsets.bottom;

迅速:

let bottomInset = UIApplication.shared.keyWindow?.safeAreaInsets.bottom

然后,您可以根据需要使用此值来调整约束或查看框架。


它适用于人像模式。风景没有顶部。

只有此解决方案对我有用。viewSafeAreaInsetsDidChange()有时根本不调用。
Murat Yasar

21

我有一个视图,它是另一个视图内的子视图。我发现我无法正确获取safeAreaInsets,即使在iPhoneX上的该视图中,即使将它放在layoutSubviews中,它也始终返回0。最终的解决方案是我使用以下UIScreen扩展来检测可以像超级按钮一样工作的safeAreaInsets。

extension UIScreen {

    func widthOfSafeArea() -> CGFloat {

        guard let rootView = UIApplication.shared.keyWindow else { return 0 }

        if #available(iOS 11.0, *) {

            let leftInset = rootView.safeAreaInsets.left

            let rightInset = rootView.safeAreaInsets.right

            return rootView.bounds.width - leftInset - rightInset

        } else {

            return rootView.bounds.width

        }

    }

    func heightOfSafeArea() -> CGFloat {

        guard let rootView = UIApplication.shared.keyWindow else { return 0 }

        if #available(iOS 11.0, *) {

            let topInset = rootView.safeAreaInsets.top

            let bottomInset = rootView.safeAreaInsets.bottom

            return rootView.bounds.height - topInset - bottomInset

        } else {

            return rootView.bounds.height

        }

    }

}

尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。
唐老鸭

谢谢您的建议。我已经编辑了答案。
Shih Ken

1
我发现,当手动添加子视图(即使它们使用自动布局)时,在某些情况下(例如表视图控制器safeAreaInsets)始终为零。使用应用程序密钥窗口返回正确的安全区域插图。好决定。
Eneko Alonso

7
当只有状态栏(iPhone 6,7等)时,这在iOS 11上不起作用。最高插入高度将返回0
Tony

您可以将它们设置为静态
Reimond Hill,

8

我尝试在视图控制器中使用“ self.view.safeAreaInset”。首先,当我在控制器的生命周期方法“ viewDidLoad”中使用它时,它是一个NSInsetZero,然后从网上搜索它并获得正确的答案,日志如下:

ViewController loadView() SafeAreaInsets :UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
ViewController viewDidLoad() SafeAreaInsets :UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
ViewController viewWillAppear() SafeAreaInsets :UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 0.0)
ViewController viewDidLayoutSubviews() SafeAreaInsets :UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)
ViewController viewDidAppear() SafeAreaInsets :UIEdgeInsets(top: 44.0, left: 0.0, bottom: 34.0, right: 0.0)

因此您可以选择需要使用safeAreaInset的正确方法并使用它!


5

Swift iOS 11,12,13+

var insets : UIEdgeInsets = .zero

 override func viewDidLoad() {
     super.viewDidLoad()

     insets = UIApplication.shared.delegate?.window??.safeAreaInsets ?? .zero
     //Or you can use this
     insets = self.view.safeAreaInsets
}

2
我们知道为什么UIApplication.shared.keyWindow停止返回安全区域插图吗?如果我们不确定将来是否可以继续使用UIApplication.shared.delegate?.window ??。safeAreaInsets,可能会冒险。我以为这是因为添加了场景,但是它们直到iOS 13才出现,所以也许不是吗?
格雷格·埃利斯

3

就我而言,我是在viewDidLoad()中添加一个UICollectionView

collectionView = UICollectionView(frame: view.safeAreaLayoutGuide.layoutFrame, collectionViewLayout: createCompositionalLayout())

不幸的是,在这一阶段,safeAreaLayoutGuide仍为零。

我通过添加解决了它:

override func viewDidLayoutSubviews() {
    collectionView.frame = view.safeAreaLayoutGuide.layoutFrame
}

2

容器视图控制器的viewDidAppear(_ :)方法,该方法扩展了其嵌入式子视图控制器的安全区域,以解决中的视图。

在此方法中进行修改,因为在将视图添加到视图层次结构之前,该视图的安全区域插图不准确。

override func viewDidAppear(_ animated: Bool) {

   if (@available(iOS 11, *)) {

     var newSafeArea = view.safeAreaInsets

     // Adjust the safe area to accommodate 
     //  the width of the side view.

     if let sideViewWidth = sideView?.bounds.size.width {
        newSafeArea.right += sideViewWidth
     }

    // Adjust the safe area to accommodate 
    //  the height of the bottom view.
    if let bottomViewHeight = bottomView?.bounds.size.height {
       newSafeArea.bottom += bottomViewHeight
    }

    // Adjust the safe area insets of the 
    //  embedded child view controller.
    let child = self.childViewControllers[0]
    child.additionalSafeAreaInsets = newSafeArea
  } 
}

2

我遇到了同样的问题。就我而言,要插入的视图在调用后将正确调整大小view.layoutIfNeeded()。将view.safeAreaInsets在此之后设置,但只有top价值是正确的。最低值仍然是0(在iPhone X上是最低)。

在尝试找出safeAreaInsets正确设置的点时,我在视图的safeAreaInsetsDidChange()方法上添加了一个断点。这被多次调用,但是仅当我CALayer.layoutSublayers()在回溯中看到该值已正确设置时。

因此,我已用view.layoutIfNeeded()的对等CALayer物替换了view.layer.layoutIfNeeded(),这导致safeAreaInsets可以立即正确设置,从而解决了我的问题。

TL; DR

更换

view.layoutIfNeeded()

通过

view.layer.layoutIfNeeded()

不起作用,如果对视图转换设置动画,则无用。
Pedro Paulo Amorim '18

1
[UIApplication sharedApplication].keyWindow.safeAreaInsets return none zero

1

当通过应用程序keyWindow请求时,尝试尝试self.view.safeAreaInsets代替UIApplication.shared.keyWindow?.safeAreaInsetsiOS 11.xx设备上的安全区域插图似乎无法填充。


0

在layoutSubviews或viewDidLayoutSubviews之前,无法保证视图布局。在使用这些生命周期方法之前,切勿依赖大小。如果这样做,您将得到不一致的结果。


0

要计算安全区域safeAreaInsets,请尝试在viewWIllAppear()中获取它,因为在didLoad()中尚未形成视图。您将在willAppear中插入正确的插图!

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.