Session 235: UIKit: Apps for Every Size and Shapegit
做者简介:@Hale 丁香园 iOS 工程师github
现现在苹果的移动设备已经不像初代的时候只有一种分辨率尺寸。iOS12 支持包括 iPhone5s、iPhone八、iPhone8 Plus、iPhone X、iPad 等各类尺寸的设备。相信必定有许多开发者对多设备的适配开发有过困扰,本 Session 对快速适配全部型号 iOS 移动设备的开发方法进行了介绍。下面介绍的属性和方法可让开发者用最短的时间让开发项目适配苹果全系列移动设备,同时还保证了用户体验不会受到影响。swift
Safe Area
在 iOS11 中被提出,它是一个很是重要的属性。相信大多数开发者对这个属性已经并不陌生,Safe Area
的提出主要是为了适配像 iPhone X 同样的全面屏。咱们能够经过 UIView
的 safeAreaInsets
和 safeAreaLayoutGuide
属性来肯定安全区域,同时安全区域限制了视图的可见部分,如 图1 所示。安全
经过下面两张图咱们能够知道,最底部视图A的安全区域如 图2 所示,随后在上添加一个不受限于视图A安全区域的浅灰色视图B,而后在视图B上添加子视图C,并设置视图C的约束依赖于视图B的 safeAreaLayoutGuide
,视图C的可视范围会被限制在 图3 黄色区域,咱们能够得出父视图的安全区域会向上传递,如 图二、3 所示。bash
UIViewController
支持使用 additionalSafeAreaInsets
属性,自定义扩展安全区域大小,以知足一些应用场景。此外 UIView
提供了 safeAreaInsetsDidChange()
方法,UIViewController
提供了 viewSafeAreaInsetsDidChange()
方法,用于检测安全区域的改变。当视图的安全区域发生改变,对应的方法就会被调用,如 图4 所示。markdown
iOS8 中提出了 layoutMargins
的概念,其主要用于设置子视图与父视图之间的边距,在 iOS11 中新增了 directionalLayoutMargins
,主要为了 Right To Left(RTL)语言下能够进行自动适配。默认状况下,layoutMargin
到各边的距离是8个点。经过在 Interface Builder
里面勾选 Constrain to margins
它会根据版本在 iOS11 及以上的系统中自动使用 directionalLayoutMargins
,如 图五、6 所示。app
正常状况下子视图的布局边距会依赖父视图的安全区域,可是当设置了insetsLayoutMarginsFromSafeArea = false
以后,子视图能够达到突破父视图安全区域的布局效果,如 图七、8 所示。iphone
当一个视图的preservesSuperviewLayoutMargins
属性为 true 时,在对它的子视图进行布局时,父视图的 margin 也会被考虑在内。若是存在一个子视图的 frame 恰好和父视图的 margin 表示区域有重合,此时设置 preservesSuperviewLayoutMargins
为 true ,则子视图会被恰好限制在父视图的 margin 内,如 图九、10 所示。ide
UIViewController
存在属性 systemMinimumLayoutMargins
,能够对其进行重写,默认状况下 view 的布局边距会受这个属性的返回值制约。以下重写了该属性,则 view 的边距最小会为 [20,20,20,20]。oop
override var systemMinimumLayoutMargins: NSDirectionalEdgeInsets { return NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 20, trailing: 20) } 复制代码
若设置 viewRespectsSystemMinimumLayoutMargins
为 false,则 view 布局边距不受 systemMinimumLayoutMargins
属性的影响,默认为 [8,8,8,8]。
iOS11 中提出了 UIScrollView
的新属性 adjustedContentInset
,它的值等于 UIScrollView
原有的 contentInset
加上 安全区域等 system inset
,如 图11 所示。
adjustedContentInset = contentInset + system inset
复制代码
本 Session 再次提到 iOS11 以后废除了原有的 UIViewController
属性 automaticallyAdjustsScrollViewInsets
取而代之的是 UIScrollView
的新增枚举 ContentInsetAdjustmentBehavior
,该枚举结构以下:
public enum ContentInsetAdjustmentBehavior : Int { case automatic case scrollableAxes case never case always } 复制代码
以下图若是设置枚举值为 .always
,则默认状况下 scrollView
的 adjustedContentInset
就等于 safeAreaInsets
,便可视区域不会被 navigationBar
和 tabBar
遮挡,如 图12 所示。
若是将枚举值设置为 .scrollableAxes
,则在能够滚动的方向上,或者设置了 alwaysBounceHorizontal/Vertical
为 true 的时候,Inset 才会生效。如 图14,页面内容比较少的时候,垂直方向上 scrollView
不可滚动,致使文本标题部分被 navigationBar
遮挡,如 图1三、14 所示。
系统默认设置的枚举值是 .automatic
, 这个枚举值基本和 .scrollableAxes
表现一致,但惟一不一样的是它还秉承了原来 automaticallyAdjustsScrollViewInsets = true
的特性,在有 navigationBar
且 isTranslucent
为 true 时,即便垂直方向上不可以滚动,依然可以调整 Inset 使内容可见,如 图15 所示。
若是将枚举值设置为 .nerver
,则 scrollView
的 Inset 不会受 safeAreaInserts
的影响而改变、如 图16 所示。
若果在一些场景下须要隐藏 status bar
,咱们通常会这么作:
class ArticleViewController: UIViewController { override var prefersStatusBarHidden: Bool { return true } } 复制代码
不幸的是在 iPhone X 上面这么写是无效的,在 iPhone X 上面只有在隐藏了 navigationBar
的前提下,上面这段代码才会生效,因此苹果官方给出的建议是同时隐藏 navigationBar
和 status bar
,如 图17 所示。
iOS9 就提出了 readableContentGuide
这一律念,主要是用于一些阅读类应用,在可视宽度较大的时候,但愿可以经过布局将阅读区域限定在必定范围,已缓解用户阅读的过程当中追踪内容移动头部所形成的疲劳。readableContentGuide
的间距大小会随着字体大小、设备不一样等因素而发生改变。现这一属性一样兼容 safeAreaInsets
。一样的 UITableView
的 cellLayoutMarginsFollowreadableWidth
属性也一样兼容 safeAreaInsets
,如 图1八、1九、20 所示。
UITableView
在iOS11开始添加了一个新的属性insetsContentViewsToSafeArea
,该属性可以控制 TableViewCell
的 ContentView
是否被 safeAreaInsets
所影响,如 图20、21 所示。
iPhone X 以后,咱们在开发过程当中常常会遇到如何布局底部按钮的问题。在本 Session 中官方给出了一种方案,例如设置按钮距底部相对于 superView 的约束为16,约束的 Priority
为 999,同时设置按钮底部相对于 safeAreaLayoutGuide
的约束值为大于等于 0。便可实现按钮在 iphone X 和 其余设备上的不一样布局,如 图23 所示。
其实本 Session 并无提出任何新的属性和方法,最新的属性在 iOS11 SDK 中就已经提出来了。可能不少开发者,在适配iPhone X 的时候遇到的问题也都解决的差很少了。但我的认为这个 Session 仍是颇有必要的,它将现有的用于适配开发的 UIKit SDK 进行了概括总结,这将有助于开发者进一步了解这些属性之间的关联关系对快速适配多种尺寸设备的项目开发会有很大帮助。
查看更多 WWDC 18 相关文章请前往 老司机x知识小集xSwiftGG WWDC 18 专题目录