iOS 11 及 iPhone X 爬坑姿势

1 序言

2017 年 9 月 12 日凌晨,苹果在乔布斯剧院发布了 iPhone X。iPhone X 正面的全面屏上方有一条刘海,对于如何适配 iPhone X,苹果的 Human Interface Guidelines 文档有给出相关的说明,但是对于前端开发者来说,我们还应该关注到更多的东西。

iOS 11 新引入了若干个 Webkit API 来解决带来的适配问题,使用这些新的 API 我们可以更好的利用 iPhone X 屏幕的特性。

2 相关概念

在了解新 API 之前需要先了解苹果设备屏幕的相关概念,接下来简单的对之进行说明。

2.1 安全区域(Safe Area)

在苹果的 UIKit文档 中,提到了安全区域。

safe_area_1

safe_area_2

简单的说,将内容放在这样的 Safe area 可以确保内容不会被设备圆角(corners),传感器外壳(sensor housing,齐刘海)以及底部的 Home Indicator 遮挡。

3 相关 API

3.1 viewport-fit

3.1.1 基础介绍

iOS 11 与早期的版本有个不同的地方,Webview 内容将会尊重所谓的安全区域。这意味着,如果你有一个标题栏固定在顶部 position: fixed; top: 0 。它将会在屏幕顶部下面的 20px 开始渲染。当你向下滚动时,它会移动到状态栏的后面。当你向上滚动时,它会再次下降到状态栏下面,而且在 20px 的间隙中,内容会透出。在 Understanding the WebView Viewport in iOS 11 这篇文章中的视频可以看到实际是怎么样的。不过这个恼人的问题通过给 viewport-fit 进行配置即可解决。

viewport-fit 可以设置可视窗口(Visual Viewport)的大小。在设备的物理屏幕上看到的初始布局视窗。在圆形屏幕上,当前屏幕上显示的部分是圆形的,但是 Viewport 却是矩形的。因此,根据 Viewport 的大小,页面的某些部可能被剪切。

clipped_area

viewport-fit 可以设置可视视窗的大小,也就是裁剪区域,viewport-fit 接收以下三个值:

  • contain:viewport 中可以显示整个页面的内容,也就是说那些 position 设置为 fixed 的元素会相对于 iOS 11 的安全区域定位。

    注意: 设置该属性值时,border-boundary: displayshape-inside: display 会失效。

  • cover:页面的内容完全覆盖整个 viewport。也就是说那些 position: fixed 的元素会相对于 viewport 定位,这就可能导致页面中有的内容会被遮盖。
  • auto:默认值,表现与 contain 一致。

CSS Round Display Level 1 中有给出了一些对于这个属性的建议:

当在非矩形显示器上设置 viewport 的边界框(viewport Bounding Box)大小时,必须考虑以下因素:

  • 由于 viewport 边界框(viewport Bounding Box)的面积大于显示区域,导致剪切区域
  • 在 Viewport 边界框和显示区域之间存在间隙

开发人员可以决定哪一个因素比较重要。如果必须确保 web 页面的没有任何一部分被隐藏,那么避免发生剪裁的优先级要高于处理在 viewport 的边界框和屏幕的边框之间的间隙。如果不想让 web 页面被缩小导致可读性上降低,那么最好将 viewport-fit 设置为 cover ,但是要注意页面被裁剪的部分。

我们直接看规范中给出的例子:

1
2
3
@viewport (viewport-fit: contain) {
/* CSS for the rectangular design */
}

viewport_fit_contain

当将 viewport-fit 设置为 contain 时,viewport 会以显示完整的、最大的矩形页面为依据,去展示 web 内容。在 iPhone X 中可视区域内多余的区域会以白色的背景色显示。

1
2
3
4
5
6
7
8
9
@viewport {
viewport-fit: cover;
}
@media (shape: round){
/* styles for the round design */
}
@media (shape: rect){
/* styles for the rectangular design */
}

viewport_fit_cover

当将 viewport-fit 设置为 cover 时,初始的 viewport 会受限于非矩形的展示区域,以填满展示区域为依据进行展示。

3.1.2 实际应用

使用 viewport-fit 只需在 viewport 的声明中增加 viewport-fit 的配置即可。

1
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"/>

通过给 viewport-fit 设置 cover 可以页面内容充满 iPhone X 的屏幕。下面是不作处理的 iPhone X 打开页面,可以看到有两条白边。

iphone_x_with_white_wings

设置了 viewport-fit 为 cover 之后:

iphone_x_with_viewport_fit_cover

3.2 safe-area-inset-*

基本介绍

新出的 iPhone X 形状不规则,其状态栏的高不再是 20px,而且在摄像头和扬声器的设置下,你的标题栏内容将会完全无法访问到。需要注意的是,这也适用于固定在屏幕底部的页脚,它将被麦克风阻塞。在一开始的关于安全区域的图中就可以看到,底部是有一定区域在 safe area 之外。

在上面一个小节,通过设置 viewport-fit 我们将页面的内容铺满了整个屏幕,但是可以看到“齐刘海”会将一部分的内容遮挡,如果我们知道屏幕边缘到 safe area 边缘的距离,那就可以通过设置 padding 属性来解决这个问题了。

在设置 viewport-fit=cover 之后,Web 中会新增四个常量:

  • safe-area-inset-top:可视区域顶部边缘到安全区域的距离(以CSS像素为单位)

  • safe-area-inset-right:可视区域最右边到安全区域的距离(以CSS像素为单位)

  • safe-area-inset-left:可视区域最左边到安全区域的距离(以CSS像素为单位)

  • safe-area-inset-bottom:可视区域底部边缘到安全区域的距离(以CSS像素为单位)

对应地苹果添加了一个方法,将安全区域布局指南暴露给 CSS 。他们添加类似一个CSS的变量概念,叫作 CSS constants 。通过 constant() 就可以获取到上面的几个常量。
通过给页面的容器元素增加以下的样式属性:

1
padding: constant(safe-area-inset-top) constant(safe-area-inset-right) constant(safe-area-inset-bottom) constant(safe-area-inset-left);

可以解决内容被遮挡的问题。

iphone_x_with_viewport_fit_cover_constant

要注意在 Darryl Pogue 的文章中说到:

Note: iOS 11.0 uses the constant() syntax, but future versions will only support env()!

在 iOS 11 中仍使用 constant(),但是在未来的版本将会抛弃 constant() 改用 env()。对此 Darryl Pogue 给出的一个关于 header 的样式例子如下,简单做了渐进增强。

1
2
3
4
5
6
7
8
9
10
11
12
13
header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 44px;

/* Status bar height on iOS 10 */
padding-top: 20px;

/* Status bar height on iOS 11+ */
padding-top: constant(safe-area-inset-top);
}

3.3 max() & min ()

3.3.1 基本介绍

max() 和 min() 未当前版本未包含的功能

如果你在网站设计中采用了 safe-area-inset-*,你可能会注意到,除了安全区域插入之外,你还需要指定最小的内填充。在上面页面中,如果我们使用了 constant(safe-area-inset-left),当设备旋转回垂直方向时,左边的安全插入距离变成了 0px,文本便紧贴着屏幕边缘了。

min()max() 来实现,这两个函数会在未来的 Safari 技术预览版中可用。这两个函数都接受任意数量的参数,并返回最小值或最大值。它们能用在calc()里面,或者彼此嵌套。并且这两个函数都允许使用calc()作为参数,就像使用数字一样。

3.3.2 实际应用

在处理左右边距时使用max():

1
2
3
4
5
6
@supports(padding: max(0px)) {
.post {
padding-left: max(12px, constant(safe-area-inset-left));
padding-right: max(12px, constant(safe-area-inset-right));
}
}

使用 @support() 做功能检测很必要。

3.4 theme color

iPhone X 在 portrait 模式下(竖屏)状态栏和 URL 地址栏会有一定的透明度,所以他们会显示半透明的网页背景色。

theme_color

通过声明 theme-color 即可:

1
<meta name="theme-color" content="#676767">

4 简单的总结

本文提及的 API:

  • viewport-fit:有 containcoverauto 三个取值。
  • safe-area-inset-*:与 safe area 有关的几个常量,在设置 viewport-fit=cover 后可以使用
  • max()min():在 CSS 中便利地获取最大值和最小值

5 补充扩展

显然除了使用以上的 API 去适配 iPhone X,还会有其他情况要使用别的适配方式。

sharp_outside

在张鑫旭大佬的这篇文章中有讲到这种效果如何实现,可以了解一下。

参考资料