Xcode 9 的安全区域

Safe Area of Xcode 9

在探索 Xcode9 Beta 时,在界面构建器视图层次结构查看器上找到 安全区域。很好奇并试图了解苹果文档上的安全区域,在要点中文档说 "The the view area which directly interacts with Auto layout" 但它并没有让我满意,我想知道这个新东西的实际使用.

有人知道吗?

Apple 文档中有关安全区域的结论段落。

The UILayoutGuide class is designed to perform all the tasks previously performed by dummy views, but to do it in a safer, more efficient manner. Layout guides do not define a new view. They do not participate in the view hierarchy. Instead, they simply define a rectangular region in their owning view’s coordinate system that can interact with Auto Layout.

Safe Area is a layout guide (Safe Area Layout Guide).
The layout guide representing the portion of your view that is unobscured by bars and other content. In iOS 11+, Apple is deprecating the top and bottom layout guides and replacing them with a single safe area layout guide.

当视图在屏幕上可见时,该指南反映视图中未被其他内容覆盖的部分。视图的安全区域反映了导航栏、选项卡栏、工具栏和其他遮挡视图控制器视图的祖先所覆盖的区域。 (在 tvOS 中,安全区域包含屏幕的边框,由 UIScreen 的 overscanCompensationInsets 属性 定义。)它还涵盖由视图控制器的 space 定义的任何其他 space additionalSafeAreaInsets属性。如果视图当前未安装在视图层次结构中,或者在屏幕上尚不可见,则布局指南始终匹配视图的边缘。

对于视图控制器的根视图,此 属性 中的安全区域代表视图控制器内容的整个被遮挡的部分,以及您指定的任何其他插图。对于视图层次结构中的其他视图,安全区域仅反映该视图中被遮挡的部分。例如,如果一个视图完全位于其视图控制器的根视图的安全区域内,则此 属性 中的边缘插入为 0.

According to Apple, Xcode 9 - Release note
Interface Builder uses UIView.safeAreaLayoutGuide as a replacement for the deprecated Top and Bottom layout guides in UIViewController. To use the new safe area, select Safe Area Layout Guides in the File inspector for the view controller, and then add constraints between your content and the new safe area anchors. This prevents your content from being obscured by top and bottom bars, and by the overscan region on tvOS. Constraints to the safe area are converted to Top and Bottom when deploying to earlier versions of iOS.


这里是现有(顶部和底部)布局指南和安全区域布局指南之间的比较(以产生相似的视觉效果)的简单参考。

安全区布局:

AutoLayout


如何使用安全区域布局?

按照以下步骤寻找解决方案:

  • 启用 'Safe Area Layout',如果未启用。
  • 删除 'all constraint' 如果它们显示与 Super view 的联系并重新附加所有安全布局锚点。或者双击约束并编辑从超级视图到 SafeArea 锚点的连接

这是示例快照,如何启用安全区域布局和编辑约束。

这是上述更改的结果


使用 SafeArea 进行布局设计
在为 iPhone X 设计时,您必须确保布局填满屏幕并且不会被设备的圆角、传感器外壳或用于访问主屏幕的指示器遮挡。

大多数使用系统提供的标准 UI 元素(例如导航栏、表格和集合)的应用会自动适应设备的新外形。背景材料延伸到显示的边缘,UI 元素被适当地插入和定位。

对于具有自定义布局的应用,支持 iPhone X 应该也相对容易,特别是如果您的应用使用自动布局并遵守安全区域和边距布局指南。


这里是示例代码(参考自:Safe Area Layout Guide
如果您在代码中创建约束,请使用 UIView 的 safeAreaLayoutGuide 属性 获取相关的布局锚点。让我们在代码中重新创建上面的 Interface Builder 示例,看看它的外观:

假设我们在视图控制器中将绿色视图作为 属性:

private let greenView = UIView()

我们可能有一个函数来设置从 viewDidLoad 调用的视图和约束:

private func setupView() {
  greenView.translatesAutoresizingMaskIntoConstraints = false
  greenView.backgroundColor = .green
  view.addSubview(greenView)
}

一如既往地使用根视图的 layoutMarginsGuide 创建前导和尾随边距约束:

 let margins = view.layoutMarginsGuide
    NSLayoutConstraint.activate([
      greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
      greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
 ])

现在,除非您的目标是 iOS 11,否则您需要使用 #available 包装安全区域布局指南约束,并回退到早期 iOS 版本的顶部和底部布局指南:

if #available(iOS 11, *) {
  let guide = view.safeAreaLayoutGuide
  NSLayoutConstraint.activate([
   greenView.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
   guide.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1.0)
   ])

} else {
   let standardSpacing: CGFloat = 8.0
   NSLayoutConstraint.activate([
   greenView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
   bottomLayoutGuide.topAnchor.constraint(equalTo: greenView.bottomAnchor, constant: standardSpacing)
   ])
}


结果:


随着 UIView 扩展,您可以轻松地以编程方式使用 SafeAreaLayout。

extension UIView {

  // Top Anchor
  var safeAreaTopAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.topAnchor
    } else {
      return self.topAnchor
    }
  }

  // Bottom Anchor
  var safeAreaBottomAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.bottomAnchor
    } else {
      return self.bottomAnchor
    }
  }

  // Left Anchor
  var safeAreaLeftAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.leftAnchor
    } else {
      return self.leftAnchor
    }
  }

  // Right Anchor
  var safeAreaRightAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.rightAnchor
    } else {
      return self.rightAnchor
    }
  }

}

这是Objective-C中的示例代码:


这是 Safe Area Layout Guide

的 Apple 开发者官方文档


需要安全区域来处理 iPhone-X 的用户界面设计。这是 How to design user interface for iPhone-X using Safe Area Layout

的基本准则

Apple 在 iOS 7 中引入了 topLayoutGuide 和 bottomLayoutGuide 作为 UIViewController 的属性。它们允许您创建约束以防止您的内容被状态、导航或标签栏等 UIKit 栏隐藏。这些布局指南已在 iOS11 中弃用,并由单个安全区域布局指南取代。

参考link了解更多信息。

The Safe Area Layout Guide helps avoid underlapping System UI elements when positioning content and controls.

安全区是系统 UI 元素之间的区域,这些元素是状态栏、导航栏和工具栏或选项卡栏。因此,当您将状态栏添加到您的应用程序时,安全区域会缩小。当您将导航栏添加到您的应用程序时,安全区域再次缩小。

在 iPhone X 上,即使没有显示栏,安全区域也会在纵向屏幕的顶部和底部边缘提供额外的插入。在横向模式下,安全区域从屏幕和主页指示器的两侧插入。

这取自 Apple 的视频 Designing for iPhone X,他们还形象地展示了不同元素如何影响安全区。

我想提一下 当我尝试调整基于 SpriteKit 的应用程序以避免圆边和 "notch" 新 iPhoneX,如最新的Human Interface Guidelines提示:UIView的新属性safeAreaLayoutGuide需要查询after视图已添加到层次结构(例如,在 -viewDidAppear: 上)以报告有意义的布局框架(否则,它只是 returns 全屏大小)。

来自 属性 的文档:

The layout guide representing the portion of your view that is unobscured by bars and other content. When the view is visible onscreen, this guide reflects the portion of the view that is not covered by navigation bars, tab bars, toolbars, and other ancestor views. (In tvOS, the safe area reflects the area not covered the screen's bezel.) If the view is not currently installed in a view hierarchy, or is not yet visible onscreen, the layout guide edges are equal to the edges of the view.

(强调我的)

如果你早 -viewDidLoad: 阅读它,指南的 layoutFrame 将是 {{0, 0}, {375, 812}} 而不是预期的 {{0, 44}, {375, 734}}

  • 早期 iOS 7.0–11.0 <已弃用> UIKit 使用 topLayoutGuide & bottomLayoutGuideUIView 属性
  • iOS11+ 使用 safeAreaLayoutGuide 这也是 UIView 属性

  • 启用文件检查器中的安全区域布局指南复选框。

  • 安全区域可帮助您将视图置于整个界面的可见部分。

  • tvOS中,安全区域还包括屏幕的过扫描插图,代表屏幕边框覆盖的区域。

  • safeAreaLayoutGuide 反映未被导航栏、选项卡栏、工具栏和其他祖先视图覆盖的视图部分。
  • 使用安全区域来辅助布局您的内容,例如 UIButton 等等

  • 在为 iPhone X 设计时,您必须确保布局填满屏幕并且不会被设备的圆角、传感器外壳或用于访问主屏幕的指示器遮挡.

  • 确保背景延伸到显示的边缘,并且垂直滚动的布局(如表格和集合)一直延伸到底部。

  • iPhone X 上的状态栏比其他 iPhone 上的要高。如果您的应用采用固定状态栏高度来将内容定位在状态栏下方,则您必须更新您的应用以根据用户的设备动态定位内容。请注意,当录音和位置跟踪等后台任务处于活动状态时,iPhone X 上的状态栏不会改变高度 print(UIApplication.shared.statusBarFrame.height)//44 for iPhone X, 20 for other iPhones

  • 主页指示器容器的高度为 34 点。

  • 启用安全区域布局指南后,您可以在界面构建器中看到安全区域限制属性。

您可以将 self.view.safeAreaLayoutGuide 的约束设置为-

ObjC:

  self.demoView.translatesAutoresizingMaskIntoConstraints = NO;
    UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
    [self.demoView.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
    [self.demoView.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
    [self.demoView.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
    [self.demoView.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;

Swift:

   demoView.translatesAutoresizingMaskIntoConstraints = false
        if #available(iOS 11.0, *) {
            let guide = self.view.safeAreaLayoutGuide
            demoView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
            demoView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
            demoView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
            demoView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
        } else {
            NSLayoutConstraint(item: demoView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: demoView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: demoView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: demoView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
        }