/ iOS

关于iPhone X 的适配

1.屏幕尺寸相关变化

  1. 高度增加了145pt,变成812pt.
  2. 屏幕圆角显示,注意至少留10pt边距。
  3. 状态栏高度由20pt变成44pt,留意这个距离就能避开“刘海”的尴尬,相应的导航栏以上变化64->88。
  4. 底部工具栏需要为home indicator留出34pt边距。
  5. 物理分辨率为1125px * 2436px.

2.横竖屏安全区对比

3.其他设备安全区域对比

4.应用设计

5.控件布局

更多可查看官方文档和视频Creating apps for iPhone X.

如何让APP适配?

APP启动样式适配

相信有一部分道友的APP在iPhone X上运行时并没有像想象中那样占满整个屏幕, 而是保持着原有的高度 在屏幕中心位置, 针对这个问题 目前经过实验得出可以通过以下方式使APP按照全屏模式运行:

  1. 通过LaunchScreen.storyboard方式启动
  2. 如果使用的是Assets中的LaunchImage, 在增加了iPhone X尺寸的图片配置后.

LaunchScreen.storyboard方式不用多说, 这里说一下如何在LaunchImage中增加iPhone X尺寸的图片配置.

准备一张尺寸:1125 * 2436的启动图片, 移动到LaunchImage的Finder目录中, 并在LaunchImage中的Contents.json文件中增加 (注意Json格式):

{
    "extent" : "full-screen",
    "idiom" : "iphone",
    "subtype" : "2436h",
    "filename" : "图片名.png",
    "minimum-system-version" : "11.0",
    "orientation" : "portrait",
    "scale" : "3x"
}

按照以上方式配置就完全解决了这个问题.

APP内部样式适配

iOS11为UIViewControllerUIView增加了两个新的属性safeAreaInsetssafeAreaLayoutGuide, 通过这两个属性我们可以获得安全区域的范围, 通过上图可以很清楚的看到安全区域的范围, 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示, 注意!这句话非常重要.

  • safeAreaInsets 适用于手动计算.
  • safeAreaLayoutGuide 适用于自动布局.

Frame布局方式适配示例

一般来说 可以通过在原有视图坐标计算时加入安全区域的范围值, 下面举个例子:

一个APP 它的NavigationBar使用的是自定义的UIView, 并非UINavigationBar, 这个自定义的UIViewframe属性为CGRectMake(0, 0, 375, 64).
这样的UIView在其他设备上是没有问题的, 标准的导航栏尺寸, 但是如果运行在iPhone X上 你会发现看上去无比的别扭, 因为异形屏幕会造成部分显示内容的遮挡问题, 这时候就要用到安全区域这个概念来解决这一问题.

先说一说通常自定义导航栏的结构, 高度是为6444高度的导航栏内容区域和20高度的状态栏区域(电池条那部分 status bar)组成, 44高度的导航栏内容区域用来显示导航栏上的控件, 如返回按钮 标题视图等等, 现在, 在iPhone X上, 原来的状态栏(status bar)高度不再考虑了, 取而代之的是安全区域顶部间距safeAreaInsets.top, 我们将原有的结构改为 安全区域顶部距离屏幕的距离safeAreaInsets.top + 导航栏内容区域44, 这样完成了一个自定义导航栏视图在iPhone X上的适配.

下面是适配前后的效果对比:

再来看一下适配后的自定义导航栏视图的UI结构

这只是举一个简单的例子, 因为不同的应用设计都是不同的, 但是适配的思路都是一样的, 还是那句话 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示, 在计算布局时 将安全区域这个新特性考虑进去, iPhone X的适配也不是难事.

说一下在代码中的安全区域的适配该如何去写:

iOS11 为UIViewControllerUIView增加了一个新的方法 - (void)viewSafeAreaInsetsDidChange;

这个方法如名字一样 在安全区域改变后会被调用, 我们可以在需要适配的UIViewControllerUIView中重写这个方法, 并且在这个方法中来根据safeAreaInsets属性更新子视图控件的布局位置.

这里有一点要注意的是当UIViewController调用- (void)viewDidLoad时它的所有子视图的safeAreaInsets属性都等于UIEdgeInsetsZero, 也就是说在- (void)viewSafeAreaInsetsDidChange;方法调用前 是无法通过当前视图控制器的子视图获取到safeAreaInsets的, 不过获取当前window对象的safeAreaInsets属性用来计算也是可以的, 但是不建议这么做, 一个视图控制器的子视图的处理当然要以它所在的控制器为准.

- (void)viewSafeAreaInsetsDidChangeUIViewController中第一次调用的时间是在- (void)viewWillAppear:(BOOL)animated调用之后, 在- (void)viewWillLayoutSubviews调用之前.

当然如果你要改变一个UIViewControllersafeAreaInsets值, 可以通过设置addtionalSafeAreaInsets属性来实现, 例如你要自定义一些特殊的样式时.

如果你改变了一个UIViewControllersafeAreaInsets属性值, - (void)viewSafeAreaInsetsDidChange也会被调用.

另外, 给道友们的分享一个获取某View安全区域范围的宏

#define VIEWSAFEAREAINSETS(view) ({UIEdgeInsets i; if(@available(iOS 11.0, *)) {i = view.safeAreaInsets;} else {i = UIEdgeInsetsZero;} i;})

使用起来比较简洁

VIEWSAFEAREAINSETS(view).left

VIEWSAFEAREAINSETS(self.view).right

AutoLayout布局方式示例

iOS11以前,我们布局时, 视图的 topbottom 一般参照的是 Top Layout GuideBottom Layout Guide.

iOS11以后, 那两个参照已经 deprecated (过时)了, 而是被Safe Area所取代.

参考自官方文档

总结

还是那句话 我们要做的是让那些不能被遮挡的内容和控件在安全区域范围内显示.

安全区域(Safe Area)概念的引入非常容易解决了异形屏幕布局的问题, 适配原有项目也不会花上很久的时间, 另外iPhone X于10月27日才开始预订, 所以我们有充足的时间来适配, 这一点相信道友们和我一样美滋滋, 最后吐槽一句, 那些黑iPhone X的无脑喷子们, 什么玩王者荣耀按不到XX键, 脑子是个好东西, 但你们不配有, 送你们一句名言 "傻人有傻福, 但傻哔没有 --------尼古拉斯·LEE".