在开放网页上,env(safe-area-inset-*) 让你的按钮避开 iPhone 的刘海、灵动岛与 Home 指示条。但在 WebView 里,同一组变量可能解析为 0,首次绘制可能忽略它们,底部操作栏——下单按钮、交易确认、连接钱包——会滑到 Home 滑动条下方,那里的点按永远不会生效。对交易 App 而言,这意味着用户最需要的控件,恰恰是够不到的那个。宿主自有 runtime 暴露原生安全区内边距,让每个引擎都尊重硬件边界。
满屏绘制是默认陷阱
iOS 把安全区作为 CSS env(safe-area-inset-top/right/bottom/left) 暴露出来。它们只有在页面声明 viewport-fit=cover 时才携带真实值——而这正是大多数全屏 App 外壳为了看起来原生所做的。一旦启用,浏览器便把内容铺到边缘,盖在灵动岛与 Home 指示条之下。如果你忘了用 env() 给固定外壳加内边距,标题就藏到「药丸」后面,底部栏滑到 Home 栏下方。Android 上 Chrome 对刘海的处理不同,许多设备上该变量解析为 0,于是同一份 CSS 在一台手机上正常,在另一台上就坏了。
WKWebView 破坏首次绘制
WebKit bug 191872 让情况更糟:在 WKWebView 里,env(safe-area-inset-*) 的值要等到页面加载后一小段时间才被设置。如果你的初始布局依赖它们,用户会先看到一闪的错误布局——CTA 压在 Home 指示条下——然后 WebKit 才追上。常见做法是加 max() 兜底,如 padding-bottom: max(12px, env(safe-area-inset-bottom)),并在加载后重新触发布局。旋转(竖→横→竖)之后,内边距值也可能卡在 0,直到下一次布局重算,于是原本正确的页面变错。
界面被遮挡时什么会断
- 下单 / 交易按钮够不到。 最主要的行动按钮压在 Home 指示条下,点按被系统手势吞掉。
- 连接钱包与确认步骤消失。 完成流程的那个按钮,恰好被硬件栏挡住。
- 标题与状态藏到灵动岛后。 标题、余额、导航控件滑到刘海切口之下。
- 横屏交易视图最易坏。 刘海转到侧边、内边距改变,但 WebView 可能不重算——侧边控件被裁切。
runtime 的解法
宿主自有 runtime 从平台读取原生安全区内边距,作为一组可靠的值喂给页面——通过桥接 API 或注入 CSS 变量——让 env() 在首次绘制与旋转后都正确解析。宿主把底部操作栏保持在 Home 指示条之上、标题避开灵动岛、布局跨方向稳定。一次集成,每个引擎,一个用户真正点得到的界面。
| 维度 | WebView | 宿主自有 runtime |
|---|---|---|
| 内边距来源 | CSS env()(可为 0) | 平台原生内边距 |
| 首次绘制 | bug 191872:空白内边距 | 首次即可靠 |
| 旋转 | 卡在 0 | 重算 |
| 底部 CTA | 压在 Home 栏下 | 避开 Home 栏 |
| 标题 | 压在灵动岛下 | 避开灵动岛 |
| 一致性 | 跨引擎不一 | 统一 |
原生 App 在每台手机上把外壳顶到 Home 指示条之上;裸 WebView 把最重要的按钮留在点按会失效的地方。runtime 给页面真实的内边距——以及一个用户够得到的界面。
参考来源
- WebKit Bug 191872 — env(safe-area-inset-*) 首次绘制未设置 — 在 WKWebView 里安全区内边距值要等加载后一小段时间才是非零,导致一闪的错误布局。
- Apple Developer — 人机界面安全区 — 用安全区内边距让内容避开刘海、灵动岛与 Home 指示条。
- meta viewport — viewport-fit=cover 与安全区内边距 —
viewport-fit=cover让内容绘入刘海/灵动岛区域,需用env()内边距保持清晰。 - WKWebView Gotchas — 安全区、键盘与旋转 — WebKit bug 191872 与内边距卡在 0 的旋转问题。
- Saabbir Hossain — 安全区内边距(刘海与 Home 条) —
env()修复方案与 iOS 11+ 对safe-area-inset-*的浏览器支持。