Apple在iOS7为UIViewController新增了topLayoutGuide和bottomLayoutGuide属性。它们可以让你创建约束以避免内容被UIKit的横条,如状态、导航或标签栏覆盖。在iOS 11这些布局指南被废弃,并被单一的安全区布局指南代替。
顶部和底部布局指南——概述
这是使用顶部和底部布局指南在导航控制器和标签栏插入视图控制器的示例:
绿色的内容视图相对底部锚点有一个顶部布局指南的顶部约束,相对顶部锚点有一个底部布局指南的底部约束。布局指南适配多种条栏的呈现及尺寸大小。
安全区布局指南
iOS 11新增的功能,苹果正废弃顶部和底部布局指南,并用单一的安全区布局指南代替:
现在我们在顶部和底部锚点的约束为安全区布局指南。这变化不大,但更容易理解。你还可以很方便的获得布局锚点在安全区的宽高以及中心。
迁移故事板
如果你已经在Storyboard里创建了约束,苹果在Xcode 9上会尝试自动做这些修改。这是我先前的示例在Xcode 8上创建的约束:
这些顶部和底部布局指南约束用了一个标准的空间常量来创建一些填充:
不知道将来会不会改变,但在使用Xcode 9 beta 2编写时,是不会收到废弃警告,除非你将部署目标更改为iOS 11.0。
苹果在WWDC 2017 Session 412 中所说,使用安全区域的Storyboard可向后部署。 这意味着即使目标为iOS 10及更早版本,也可以在Interface Builder切换使用安全区布局指南。
你可以通过更改Storyboard文件检查器的设置,将顶部和底部布局指南转换为安全区布局指南。 需要为项目的每个Storyboard执行此操作。
Storyboard自动将顶部和底部布局指南替换为安全区,并更新约束:
缺少间距
不幸的是,迁移不是没有问题。 对安全区布局指南的约束似乎不符合标准间距。 检查约束显示为标准间距:
布局运行起来确是缺少间距:
这似乎是Interface Builder中的一个bug(rdar://32970194),因为它不允许使用“标准”间距为安全区布局指南创建约束。 解决方法是手动将缺少的间距添加到顶部和底部约束:
希望Apple在未来的更新中修复这一点,因为在大项目里手动修复这个很痛苦。
代码里创建约束
如果在代码创建约束,请使用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)
])
}
注意点:
- safeAreaLayoutGuide是UIView的属性,而topLayoutGuide和bottomLayoutGuide是UIViewController的属性。 iOS 11的beta版在UIViewController上也有一个safeAreaLayoutGuide属性,但已废弃。
- constraintEqualToSystemSpacingBelow方法在iOS 11是新增的,不需要硬编码标准间距。还有小于或大于版本。 对于水平间距也有constraintEqualToSystemSpacingAfter。
- 如果自定义工具栏,可以使用UIViewController上的additionalSafeAreaInsets属性来增加安全区的大小。