在iPhoneX上适配你的App

作者: 暮落晨曦 | 来源:发表于2017-09-30 16:29 被阅读1027次

    在iPhone X上构建你的APP

    原文链接,文章为 Building Apps for iPhone X Fall 2017 - Session 201 - iOS 的文字实录。以视频中主人公为第一视角,结合本文作者的一些理解的进行了内容讲解。

    iPhone X

    前言

    要适配你的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:

    iPhoneX

    同其他 iPhone 或 iPad Simulator 一样,你可以直接使用系统内置 App,这样就可以很好的通过实例观察不同的 UIKit 组件在 iPhone X 上的表现。


    比如文件App,就展示了很多最新的 iOS11 API 的实际应用。比如一体式的 SerchBar 和 Large NavigationBar Titles 。


    别忘了,你还可以在 Simulator 中登录 iCloud 帐户,并访问你的 iCloud Drive 。这样你就可以方便的将文件或者照片等测试文件传输到 Simulator 中。

    iCloud

    另外一个不错的例子就是通讯录,它展示了 TableView 如何在 iPhone X 上呈现。一定要将 Simulator 旋转至横向模式。这样就可以看到一些效果,比如 Section Header 横跨屏幕,而 TableViewCell 则遵照 SafeArea 的原则,并保持缩进。稍后还会讲到 TableView 。

    Address
    接下来我们来看看我负责的项目 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 相同

    1. 先勾选 Use Safe Area Layout Guide
    2. 再修改 PageControl 的 bottom 约束,将 SuperView 改成与 Safe Area

    操作步骤如gif图


    SearchBar 适配问题

    接下来,来看看我遇到的第二个问题。问题出在 Videos 标签页,同样看上去也还不错,但当我调出 SearchBar 时,看起来位置似乎有问题,让我们和通讯录进行一下对比。


    SearchBar 的背景颜色似乎不太对,Size 也不太对。如果我旋转到横屏模式,可以看出 SearchBar 和 Cancel 按钮都被屏幕的圆角裁剪掉了一部分。


    这个例子说明 Safe Area 的存在显得至关重要,对于这种搜索栏 WWDC 的做法是直接显示了一个 UISearchController ,而在 iOS 11 中 SearchBar 可以集成在 NavigationBar 中,并且给出正确的显示方式。让我们来看下代码如何修改:


    这是显示 SearchController 的代码,需要做两处改动。

    1. 将 searchController 赋值给 navigationItem.searchController
    2. 让 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 ,自由计算所需布局的数据
    • 不要让控件遮挡屏幕底部的 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, 如需转载请联系作者并标明出处,谢谢

    相关文章

      网友评论

        本文标题:在iPhoneX上适配你的App

        本文链接:https://www.haomeiwen.com/subject/fkvuextx.html