在iPhone X上构建你的APP
原文链接,文章为 Building Apps for iPhone X Fall 2017 - Session 201 - iOS 的文字实录。以视频中主人公为第一视角,结合本文作者的一些理解的进行了内容讲解。
前言
要适配你的App,只需按照 iOS11 SDK 进行修改,就可以充分利用iPhone X搭载的超视网膜显示屏。
如果你的App主要基于标准的 UIKit 控件,并且使用 AutoLayout 那么接下来的任务就会很轻松了,因为绝大多数工作都由 UIKit 为你代劳了。
如果你使用的是自定义控件或没有使用 AutoLayout ,再或者你的 App 是一款自定义全屏的 App 像很多游戏那样,你也不必担心,虽然你确实有些工作要做。但整个适配过程中并没有什么难点,而且我们有很多内建支持工具,比如全新推出的 Safe Area Layout Guide 。
无论如何,你都需要全面测试你的 App,尤其是在横屏模式下,以确保万无一失。
iPhone X Simulator
最新版 Xcode 包含支持 iPhone X 的 模拟器 让你可以改变绝大多数的布局, 尤其是调整关于 Safe Area 的布局。对于一些 App, 比如使用了 Metal 或是使用了前置摄像头等硬件功能的 App ,最好在实际设备上进行测试。
让我们来看看全新的 iPhone X Simulator:
同其他 iPhone 或 iPad Simulator 一样,你可以直接使用系统内置 App,这样就可以很好的通过实例观察不同的 UIKit 组件在 iPhone X 上的表现。
比如文件App,就展示了很多最新的 iOS11 API 的实际应用。比如一体式的 SerchBar 和 Large NavigationBar Titles 。
别忘了,你还可以在 Simulator 中登录 iCloud 帐户,并访问你的 iCloud Drive 。这样你就可以方便的将文件或者照片等测试文件传输到 Simulator 中。
另外一个不错的例子就是通讯录,它展示了 TableView 如何在 iPhone X 上呈现。一定要将 Simulator 旋转至横向模式。这样就可以看到一些效果,比如 Section Header 横跨屏幕,而 TableViewCell 则遵照 SafeArea 的原则,并保持缩进。稍后还会讲到 TableView 。
接下来我们来看看我负责的项目 WWDC App,我花了一点时间,让它适配 iPhone X,我想分享一下我遇到的有关布局的问题以及我的解决方案。
适配 iPhone X
WWDC是一款真实存在的App,它已经面世了很多年。这些年来,很多工程师都参与了它的编写。它既有很多标准控件和 AutoLayout,也包含自定义View。App 中较老的部分甚至使用了手动布局。我会用这款 App 来强调三处需要针对 iPhone X 进行适配的地方。
首先,我在 Xcode 9 中打开工程文件,将 Base SDK 设为 iOS11,这样就可以以原生分辨率运行 App 了。
当设置你的 App 时,如果你发现 App 没有完整在 iPhone X 下运行,请检查一下你是否配置了 Launch Storyboard,因为这部分设置是必须的。(编辑注: 如果没有使用Launch Storyboard的话)
我们的初始视图是 Videos 标签页,效果如下图看起来还不错。这些全部使用的是今年的新代码,其中使用了遵循 AutoLayout 的 UICollectionView,以及 UINavigationBar 和 UIToolBar 控件等。所以绝大多数界面的布局都没问题,因为 UIKit 为我代劳了大部分工作。
有一个地方没有使用 AutoLayout ,那就是 News 标签页。效果如下图,其实这个 View 看起来还不错,尽管所有 UI 都是手动布局,尽管我们没有直接使用 AutoLayout,负责布局的代码也会注意到 layout margin insets,UIKit 会自动调整布局适应 Safe Area
。
AutoLayout 适配 Safe Area
我遇到的第一个适配问题就是再 News 标签页中的全屏图片浏览器。尽管这个 View 使用了 AutoLayout ,但其中 PageControl 的位置太靠下了,已经与主屏幕指示器重叠在了一起。
这里的主要问题在于页面空间的底部约束关联的是 SuperView ,也就是 Home 指示器后面的整个屏幕。所以,我们不应该根据父视图进行约束,而应该将 PageControl 按照底部的 Safe Area Layout Guide
进行约束。修改方式如下:
在调整约束前,需要先启用 StoryBoard/Xib 的 Safe Area Layout Guide
。Xcode 9 以前的 StoryBoard/Xib 不会自动启用该选项。需要进入 文档检查器 -> Interface Builder Document -> 勾选 Use Safe Area Layout Guides
复选框
注意: iOS StoryBoard 打开
Use Safe Area Layout Guides
功能会自动升级绑定在 top 和 bottom 的 layout guide 约束,leading Edge 以及 trailing Edge。因此,勾选后一定要检查测试所有 AutoLayout 的约束。
Storyboard Safe Area
将如图所示右侧的 Use Safe Area Layout Guide
勾选上。
勾选之后,效果如下:
多出一个叫 Safe Area 的区域,如上图所示。
此时我们来看下 PageControl 的约束,之前约束都是与 SuperView 构建的关系,现在全部变成了 Safe Area。这样就不会遮挡 Home 指示器了。 怎么样很简单吧?
Xib Safe Area
操作步骤与 Storyboard 相同
- 先勾选
Use Safe Area Layout Guide
- 再修改 PageControl 的 bottom 约束,将 SuperView 改成与 Safe Area
操作步骤如gif图
经过上述调整,我们的 PageControl 就已经不再遮挡 Home 指示器了,并且在横竖屏下均有效。
SearchBar 适配问题
接下来,来看看我遇到的第二个问题。问题出在 Videos 标签页,同样看上去也还不错,但当我调出 SearchBar 时,看起来位置似乎有问题,让我们和通讯录进行一下对比。
SearchBar 的背景颜色似乎不太对,Size 也不太对。如果我旋转到横屏模式,可以看出 SearchBar 和 Cancel 按钮都被屏幕的圆角裁剪掉了一部分。
这个例子说明 Safe Area 的存在显得至关重要,对于这种搜索栏 WWDC 的做法是直接显示了一个 UISearchController ,而在 iOS 11 中 SearchBar 可以集成在 NavigationBar 中,并且给出正确的显示方式。让我们来看下代码如何修改:
这是显示 SearchController 的代码,需要做两处改动。
- 将 searchController 赋值给 navigationItem.searchController
- 让 searchController 变为活跃状态
注意: 该过程只在 iOS 11 下有效,因此,其他版本保持原有行为。
修改前:
fileprivate func presentSearchController(initialSearchTeat searchTest: String? = nil) {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self as? UISearchResultsUpdating
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.text = searchTest
present(searchController, animated: true, completion: nil)
}
修改后:
fileprivate func presentSearchController(initialSearchTeat searchTest: String? = nil) {
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self as? UISearchResultsUpdating
searchController.obscuresBackgroundDuringPresentation = false
searchController.searchBar.text = searchTest
if #available(iOS 11.0, *) {
self.navigationItem.searchController = searchController
searchController.isActive = true
} else {
present(searchController, animated: true, completion: nil)
}
}
现在 SearchBar 刚好在 Safe Area 中,并且这全部是 NavigationBar 自动帮我们处理的。如果你的 UI 效果中有 SearchBar 在 navigationBar 上的话,你一定要在 iOS 11 上做类似的处理。
TableView 适配问题
现在我们来看看 App 中的第三处改动,需要改进的地方。
如下是 Schedule 标签页,我们使用了 UITableView ,布局在竖屏模式下看起了不错,但这里搜索栏的样式也不太对。这个搜索栏恰好是作为 Header 视图插入到 TableView 中的。但我们可以像刚才那样改动,也就是让 SearchBar 集成到 NavigationBar 中。
布局切换到横屏模式所有UI看上去都遵循了 Safe Area 布局,但仔细观察 TableView 的 SectionHeader ,它自定义的 BackgroundColor 似乎有问题,颜色应该像通讯录 App 里的 TableView 那样一直延伸到屏幕边缘。
运行下代码会发现,App 将背景颜色设置给了 headerView 的 contentView ,这看上去很合理。
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
let font = UIFont.preferredFont(forTextStyle: .headline)
header.textLabel?.font = font
header.contentView?.backgroundColor = UIColor.lightGray
}
事实上,在除了 iPhone X 之外的 iPhone 上都没有这个问题。那问题出在了哪里呢?
我们要研究下 TableView 在 iPhone X 上是如何布局 Cell 的。
原理
为了帮助大家理解,我们通过 Xcode 的 View Hierarchy Debugger 进行视图层级的查看。
这是我们刚刚看到的视图,通过 View Hierarchy Debugger 可以调节视图的层级和控制视图的显示/隐藏。
只显示 TableView ,你会发现它的尺寸是整个屏幕。
调节可见范围来显示 TableView 的 Cell 。
你会发现 Cell 是与屏幕一样宽的。
选中其中一个 Cell。
再用 Safe Area 来表示它的位置。
继续调节可见范围,我们可以看到 Cell 的 contentView ,自动布局在了 Safe Area 中。
虽然 Cell 的 Size 与屏幕一样,但 Cell 的 contentView 却和 Safe Area 的 Size 相同。
这样发生了刚刚我们发现的问题。
刚刚我们看到的界面有些混乱,我们进行一些简化并加上一些标记。
默认情况下,TableViewCell 会包含 ContentView,这样就可以将内容适配在 Safe Area 内部。但这种行为是可以由你控制的。
在 Xcode 中你可以勾选 ContentView 的
Insets To Safe Area
选项,代码中也有对应的属性可以设置。如果不勾选或不设置,contentView 就不会适配 Safe Area,而是会与 cell 一样大小。无论 ContentView 如何设置,它的 Layout Margin 始终默认与 Safe Area 关联。与 ContentView 适配类似,也有一些属性可以让你控制 Layout Margin 。关于这一点以及其他与边距相关的选项你可以查阅文档以及 WWDC 视频。
解决方案
已经知道了原因是 Cell 的 contentView 的 Size 是与 Safe Area 相同的。通过代码我们可以了解到,我们只设置了 contentView 的背景颜色。此时,我们有几种解决方案来解决这个问题。
其中一种是禁用 TableView 的默认将 ContentView 适配 Safe Area 的行为,但这样会影响 contentView 里的其他内容。
这里最好的解决方案就是设定 backgroundView 的 backgroundColor。backgroundView 与 contentView 不同,它与 Cell 是一样大小,不受 Safe Area 影响。
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
let header = view as! UITableViewHeaderFooterView
let font = UIFont.preferredFont(forTextStyle: .headline)
header.textLabel?.font = font
header.backgroundView?.backgroundColor = UIColor.lightGray
}
修改完毕后,编译运行。颜色就充满了整个 Cell 。但 ContentView 中的内容并没有发生变化。
以上就是我在为 WWDC 适配 iPhone X 的时候遇到的三个问题示例。
总结
适配 iPhone X ,需要注意以下几点:
- 遵循 iOS 11 SDK,使用 Launch Storyboard,可以让你的 App 与原生分辨率一致
- 测试UI时,横竖屏幕都要进行,绝大多数问题出在横屏下(左右横屏都要测)
- 遵循 Safe Area 可以避免绝大多数适配问题
- AutoLayout: 设置
safeAreaLayoutGuide
- 手动布局: 使用
safeAreaInsets
,自由计算所需布局的数据
- AutoLayout: 设置
- 不要让控件遮挡屏幕底部的 Home 指示器
关于 Home 指示器以及 iPhone X 设计方面的内容,请查看
Session Name | Session Number | From |
---|---|---|
Designing for iPhone X | Session 801 | Fall 2017 |
AutoLayout Techniques in Interface Builder | Session 412 | WWDC 2017 |
Modern User Interaction on iOS | Session 219 | WWDC 2017 |
Updating Your App for iOS 11 | Session 204 | WWDC 2017 |
一定要看 Designing for iPhone X 这个 Session,因为有很多关于 iPhone X 适配的细节包含在其中。
版权声明: QC-L, 如需转载请联系作者并标明出处,谢谢