【问题标题】:safeAreaInsets in UIView is 0 on an iPhone XUIView 中的 safeAreaInsets 在 iPhone X 上为 0
【发布时间】:2018-04-12 11:19:28
【问题描述】:

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

谢谢!

【问题讨论】:

  • 您可以随时随地使用:` if #available(iOS 11.0, tvOS 11.0, *) { return UIApplication.shared.delegate?.window??.safeAreaInsets != .zero } return false `SOURCE
  • 根据下面的火星回答,我还看到获得这些的唯一可靠方法是在 viewDidLayoutSubviews() 或 viewDidAppear 中。实际上,“覆盖 func viewSafeAreaInsetsDidChange()”似乎效果最好。

标签: ios iphone swift iphone-x


【解决方案1】:

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

【讨论】:

  • 如果您在UIViewController 中使用view,您也可以在viewDidLayoutSubviews() 中使用它,它应该像我一样工作。
  • 注意 viewWillLayoutSubviews 子视图的 saveAreaInsets 值错误。
  • 也可以在 UIViewController.viewDidLayoutSubviews 中进行。
【解决方案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
  } 
}

【讨论】:

    【解决方案3】:

    我有一个视图,它是另一个视图中的子视图。 我发现我无法正确获取 safeAreaInsets,它总是返回 0,在 iPhoneX 上的那个视图中,即使我把它放在 layoutSubviews 中。 最终的解决方案是我使用以下 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
    
            }
    
        }
    
    }
    

    【讨论】:

    • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
    • 感谢您的建议。我已经编辑了我的答案。
    • 我发现在手动添加子视图时(即使它们使用自动布局),在某些情况下,比如表格视图控制器,safeAreaInsets 总是为零。使用应用程序密钥窗口返回正确的安全区域插图。好电话。
    • 这在 iOS 11 只有状态栏时不起作用(iPhone 6,7,...)。它将为顶部插入高度返回 0
    • 你可以把它们变成静态的
    【解决方案4】:

    我遇到了同样的问题。在我的情况下,我插入的视图将在调用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()
    

    【讨论】:

    • 不工作,如果您正在为视图转换设置动画,则无用。
    【解决方案5】:

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

    对象-C:

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

    斯威夫特:

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

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

    【讨论】:

    • 它适用于纵向模式。景观没有顶部的视图。
    • 只有这个解决方案对我有用。有时根本不会调用 viewSafeAreaInsetsDidChange()。
    • 这个解决方案对我有用!谢谢兄弟。
    【解决方案6】:

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

    我通过覆盖 viewSafeAreaInsetsDidChange 并在其中进行布局来解决它:

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

    【讨论】:

    • 同上 - 这是正确的 - 尽管这种情况有点愚蠢
    • 如果 OP 想要在自定义视图而不是视图控制器中执行此操作,那么还有 UIView 方法 safeAreaInsetsDidChange
    • 如果您在视图控制器中配置单元格,这是完美的
    • 我已更改为横向模式,这是唯一对我有用的答案!
    • 像魅力一样工作。不知道在不知道这种方法的情况下,我什至是如何在过去 5 年中幸存下来的:/
    【解决方案7】:
    [UIApplication sharedApplication].keyWindow.safeAreaInsets return none zero
    

    【讨论】:

      【解决方案8】:

      试试self.view.safeAreaInsets 而不是UIApplication.shared.keyWindow?.safeAreaInsets 当通过应用程序 keyWindow 请求时,iOS 11.x.x 设备上的安全区域插图似乎无法填充。

      【讨论】:

        【解决方案9】:

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

        【讨论】:

          【解决方案10】:

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

          【讨论】:

            【解决方案11】:

            我尝试在视图控制器中使用“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 的正确方法并使用它!

            【讨论】:

              【解决方案12】:

              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
              }
              

              【讨论】:

              • 我们知道为什么 UIApplication.shared.keyWindow 停止返回安全区域插图吗?使用 UIApplication.shared.delegate?.window??.safeAreaInsets 似乎有风险,如果我们不能确定这将在未来继续工作。我假设这是因为添加了场景,但它们直到 iOS 13 才出现,所以可能不会?
              【解决方案13】:

              在我的例子中,我在 viewDidLoad() 中添加了一个 UICollectionView

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

              不幸的是,现阶段 safeAreaLayoutGuide 仍然为零。

              我通过添加解决了它:

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

              【讨论】:

                猜你喜欢
                • 2021-10-04
                • 1970-01-01
                • 2020-03-31
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多