关于 SafeArea 和 适配 iPhoneX

之前写了一篇适配 iOS 11 的文章链接,如今 iPhone X 上市后,又要掀起一波适配潮了。对于开发者来说, iPhone X 并不像之前的产品那么容易适配。因为齐刘海、虚拟 HomeIndicator,可以说是相当有特点了。那么,X 到底做了哪些改变,怎么适配,下面我们就来详细谈谈。

iPhone X 的一些改变

关于 iPhone X(后文中简称为 X)的屏幕尺寸,由于个性的刘海以及取消了物理 Home 按键而用虚拟按键 Home Indicator 取代,所以理所当然屏幕的高度会高一些。我们知道,6、7、8的尺寸都一样,是375 * 667 pt,而 X 的尺寸是 375 * 812 pt,宽度跟前者一样,由于是 @3x,所以像素尺寸是1125 * 2436 px。即加长版的 iPhone 8。具体见下图

size_diff.png

关于细节高度变化。Status Bar 的高度由之前的 20 pt 变为 44 pt,增加了 24 pt 是为了适应刘海对顶部空间的影响。
如果没有 Large Title,导航栏加状态栏的总体高度是 44 + 44 = 88 pt。如果有 Large Title,总体高度达到 44 + 52 = 96 pt。这跟之前的 64 pt 还是有很大差距的。
另外,底部的虚拟键 Home Indicator 的高度是 34 pt,适配的时候注意内容不能跟他有视图层叠,否则会很影响显示效果。底部的总高度为 49 + 34 pt = 83 pt。(其中 49 为 Tab Bar 高度)。

从上面数据不难看出,X 的改变甚至比之前的 4 -> 5 的差别都大。
好消息是,大多数使用标准的、系统提供的 UI 元素(如 UINavigationBar、UITableView、UICollectionView)将会自动适配 iPhone X。背景将会延伸到屏幕的边界,UI 元素将会被合适地放置。比如 Navigation Bar 的背景色会自动延伸到 Status Bar 中,Tab Bar 、Tool Bar 会被合适的放置在 Home Indicator 上面,其背景色也会延伸到屏幕底端。

当然,现在大多项目中的很多控件都是各种自定义,而且上述系统控件也不是百分百场景下都能适配的,所以为了完美适配 iPhone X,只能老老实实跟着笔者认识一下苹果的这套适配方案,适配工作才可能开展的顺利一些。

修改启动图

适配过 iPhone 4 到 iPhone 5 的开发者应该都知道,当年由于屏幕尺寸的较大变化,如果没做适配在 iPhone 5 运行就会有大大的黑边。解决方案就是配置启动图(LaunchScreen.storyboard 启动除外)。X 也是一样。
如果项目使用的是图集(Launch Images Asset),直接让设计师多做一张像素 1125 * 2436 px 的启动图即可。
如果是用的故事板启动图(Launch Screen.storyboard),则无需过多设置。
接下来,编译运行一下项目,就没有黑边了,但取而代之的会看到顶部和底部拥挤的布局,那么我们下面来具体谈一下怎么适配。

通过 Safe Area 来适配 X

谈到适配 iOS 11 || iPhone X,不得不认识一下 Safe Area。Safe Area 的出现也是为了给适配 iPhone X 工作提供方便。这部分我会尽可能详细介绍一下 Safe Area,掌握了 Safe Area,也就基本完成了 X 的 UI 适配工作。

认识 Safe Area 之前,我们先花两分钟时间复习一下 topLayoutGuide 和 bottomLayoutGuide。
iOS 7 之后苹果给 UIViewController 引入了 topLayoutGuide 和 bottomLayoutGuide,目的就是保护内容不被各种 Bar 遮挡,如(StatusBar, NavBar, ToolBar, TabBar)。这个属性有一个 length 值( 如 topLayoutGuide.length),这个值可能由当前的 ViewController 或者 NavigationController 或者 TabbarController 决定。

一个独立的ViewController,不包含于任何其他的ViewController。如果状态栏可见,topLayoutGuide表示状态栏的底部,否则表示这个ViewController的上边缘。
包含于其他ViewController的ViewController不对这个属性起决定作用,而是由容器ViewController决定这个属性的含义:
如果导航栏(Navigation Bar)可见,topLayoutGuide表示导航栏的底部。
如果状态栏可见,topLayoutGuide表示状态栏的底部。
如果都不可见,表示ViewController的上边缘。
这部分还比较好理解,总之是屏幕上方任何遮挡内容的栏的最底部。

这两个属性是 VC 的属性,平常用的场景可能不是太多。但是仅仅描述上和下的安全内边距在 iPhone X 之前是足够使用的,因为不管横屏还是竖屏,只需知道上下安全内边距即可。但是在 X 上就行不通了,竖屏状态大多场景下还好,可是横屏情况下,要想适配酷酷的刘海和 Home Indicator,仅仅这两个属性就力不从心了。苹果只好重新构思这块代码,自然就有了SafeArea 的解决方案(相信如果让你来设计这块代码来帮助广大开发者适配 X ,也应该很快想到类似方案)。
SafeArea,顾名思义,是安全区域。原则上所有的控制视图都应该在这上面绘制,超出这个区域就会有可能被 NavBar 或 StatusBar 或 ToolBar 或 Home Indicator 等遮挡而影响交互体验。看下面这张官方文档的示例图:


SafeArea_AppleDoc.png

在 iOS 11 中,苹果置过期了上面的控制器 VC 属性 top/bottomLayoutGuide,取而代之的是 View 属性 safeAreaLayoutGuidesafeAreaInsets。下面就细细说一下苹果的这套 SafeArea 解决方案如何。
我们先说一下safeAreaInsets

SafeAreaInsets

顾名思义,它就是安全区域的内边距。
简而言之,它的值是(StatusBarHeight + NavBarHeight, 0, HomeIndicatorHeight + TabBarHeight, 0)
在 X 上运行,即(44 + 44, 0, 34 + 49, 0)。(当然,上面的公式中的控件都没有隐藏。)
但是要注意,SafeAreaInsets这个属性的取用时机要当心。在ViewDidLoad中这个值是 zero 的,通过下面打印,可以看出端倪

viewDidLoad                              => {0, 0, 0, 0}
viewWillAppear:                         =>{0, 0, 0, 0}
viewSafeAreaInsetsDidChange =>{44, 0, 34, 0}
viewWillLayoutSubviews           =>{44, 0, 34, 0}
viewDidLayoutSubviews            =>{44, 0, 34, 0}
viewDidAppear:                          =>{44, 0, 34, 0}

综上,可以在viewSafeAreaInsetsDidChange方法中取值(view也有个类似方法,safeAreaInsetsDidChange),也可以在viewWillLayoutSubviews取值。

我们如果之前用过 top/bottomLayoutGuide,就会很自然就知道了SafeAreaInsets的用法。很类似,这个属性就是告诉开发者,添加的控件最好在这个安全内边距区域内。view.safeAreaLayoutGuide.layoutFrame就是通过该内边距计算获得。

  • 用 Xib 创建视图的话,如果是 iOS 9 +,那么很简单直接将控件的相对布局对象改为safe area即可。
  • 如果不是 Xib 视图,或者要支持 iOS 8 +,那么用代码也很简单。以控制器为例,向控制器视图中添加子控件布局的时候,布局不要直接以 View 为参照视图,要用到SafeAreaInsets
    UIEdgeInsets insets = self.view.safeAreaInsets;
        [label mas_makeConstraints:^(MASConstraintMaker *make) {
            // ...
            make.bottom.mas_equalTo(-insets.bottom);
        }];
    
AdditionalSafeAreaInsets

当 view controller 的子视图覆盖了嵌入的子 view controller 的视图的时候。比如说, 当 UINavigationController 和 UITabbarController 中的 bar 是半透明(translucent) 状态的时候, 就有 additionalSafeAreaInsets。

You might use this property to extend the safe area to include custom content in your interface. For example, a drawing app might use this property to avoid displaying content underneath tool palettes.

官方文档举的例子是在一个画画 App 中通过该属性来延伸安全区域,从而避免画笔画到了画板上。
注意这个属性是在原来的基础上叠加多少,不是改为了多少。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,565评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,021评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,003评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,015评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,020评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,856评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,178评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,824评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,264评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,788评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,913评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,535评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,130评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,102评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,334评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,298评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,622评论 2 343

推荐阅读更多精彩内容