深入理解 ios11 webview 的 viewport

深入理解 ios11 webview 的 viewport

ios11 带来了一些新的东西,凭直觉,围绕状态栏区域的展现对于程序员使用的像 Apache Cordova 或 Ionic 这样的工具显得尤为重要。尤其是,这个改动影响到所有为 ios11 构建的、基于 web 的、使用了固定标题栏的 app 之中。这篇文章帮助你理解 ios11 webview 的 viewport。

注: 现有的 app 可以继续正常工作,因为原有的 viewport 表现并没有被改变。这个改变只影响那些使用 xcode9 来为 ios11 构建的应用。

为了理解这个改动,我们需要先了解一下背景知识。

状态栏和安全区域

在 ios 的早期版本中,状态栏是屏幕顶部的不可触摸的黑色长条。它是系统 UI 的一部分,而 app 运行在它的下面。

随着 ios7 的发布,一切发生了改变。它变成了一个透明的状态栏,从而显示了 app 导航栏的颜色。对于像 Cordova 这样显示在 webview 的 app, 意味着通常需要检测是否 ios 并添加 padding-top: 20px 到固定的头部导航, 以便正确填充那部分空间。

后来发布的较新版本的 ios 添加了一些小修订。包含了一些新功能,比如当有电话呼入或在后台使用定位功能时顶部会展示额外的状态栏。

在原生应用方面,存在大量的导航栏和自动布局指南。会有一些布局指示在屏幕的顶部或底部以便自动调整高度,让状态栏不会遮住应用。如果在顶部设置了 UINavigationBar, ios 会自动在状态栏拓展它的颜色。对于 web,很不幸,没有等价的办法。

iOS11 的改变

The default viewport behaviour in iOS 11 on an iPhone 8.

ios11 和先前版本的不同之处在于, webview 内容现在重视安全区域了。这意味着,如果设置头部栏 top: 0, 它会渲染在屏幕顶部的20px之下,也就是状态栏下面。当向下浏览时,会移到状态栏后面。当向上浏览时,又会移动状态栏下面与状态栏部对齐。(留下尴尬的20像素空白)

可以看看下面剪辑的视频中了解到有多么糟糕:

为什么苹果公司会改成这个样子

如果你看过 iPhone X 的设计,就知道它这样改动是有道理的: iPhone X 的屏幕顶部有个为摄像头和话筒留出的不规则区域。如果真的渲染到屏幕顶部,就会被麦克风挡住。

通过对齐状态栏底部,可以确保头部栏可以一直被访问。

酷!…除了现在 app 上面滑动内容时在状态栏底下留下的尴尬的空白。

iOS11 解决方案

值得庆幸的是,Apple 给我们提供了一个通过设置 viewport meta 标签的解决方案。更幸运的是,它还向下兼容旧的,过期的 UIWebView。

我们正在找的 viewport 选项就是 viewport-fit,它有三个可选值:

  • contain: 这意味着 iOS11 下,固定栏会被包含在安全区域内。
  • cover: 这意味着固定栏是相对 viewport 固定的,就算会被立大状态栏遮蔽。这就回到了 iOS10 的表现。
  • auto: 这和 contain 选项的表现是一样的。

所以为了恢复到固定在屏幕的最顶端,就像 iOS10 一样在状态栏下面。可以添加 viewport-fit=cover 到 viewport meta 标签。

iPhone X

但 iPhone X 的小刘海怎么办?这个状态栏不再是 20px 高,并且因为里面有摄像头和扬声器,头部栏会完全无法访问。同样要注意固定在屏幕底部的页脚,它会被麦克风遮住。

注:如果有启动脚本,应用将只会运行在 iPhone X 的全屏幕上。现有的 app 会在顶部和底部有黑色区域。

幸运的是,Apple 给出了一个关于安全区域的 css 指南。他们添加一个类似 css 变量,叫做 css 常数。可以把它看做是系统的 css 变量并且不可覆盖。可以在 css 中通过constant() 函数来调用它们,并且已向 css 标准化工作小组提案。

这四个布局常量是:

  • constant(safe-area-inset-top): viewport 顶部插入的安全区域(使用 css 像素)
  • constant(safe-area-inset-bottom): viewport 底部插入的安全区域(使用 css 像素)
  • constant(safe-area-inset-left): viewport 左边插入的安全区域(使用 css 像素)
  • constant(safe-area-inset-right): viewport 右边插入的安全区域(使用 css 像素)

这是 Apple 给我们的最后一个礼物,也是向下兼容的。

css constants 的例子

假设有一个头部固定栏,现在 iOS10 下的 css 如下:

header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 44px;

    padding-top: 20px; /* Status bar height */
}

为了调整自适应 iPhone X 和 iOS11 设备,会添加 viewport-fit=cover 选项到 viewport meta 标签上,并且更改 css, 引用 css constant。

header {
    /* ... */

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

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

重要的是,要保留回退值,因为旧设备不能解释 css constant() 语法。别外,也可以在 css calc() 表达式中使用 constants.

记住,你也会想要在屏幕底部做这件事的。

0%