iPhone Safe Area 安全区域适配解决方案
问题描述
iPhone X 系列及更新设备引入了"刘海"和底部"Home Indicator",导致网页内容可能被遮挡或与底部留有过大间距。
解决方案概述
1. iOS 原生项目修改
文件: t-ios/t/ViewController.swift
修改内容:
- 使用 Auto Layout 约束替代手动设置 frame
- WebView 顶部适配到 safeAreaLayoutGuide
- 底部延伸到屏幕底部,由 H5 页面处理安全区域
关键发现 :iOS项目中WebView使用的是 self.view.bounds ,这意味着WebView占据了整个视图控制器的边界, 包括安全区域 。
override func viewDidLayoutSubviews() {
webView.frame = self.view.bounds
}
//改为:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// WebView 现在使用 Auto Layout 约束,不需要手动设置 frame
}
// 设置 WebView 约束,确保适配安全区域
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
webView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor),
webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), //注意左右没有留安全边距
webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)//注意左右没有留安全边距
])
// 设置 WebView 约束,确保适配安全区域;左右方向也留有安全距离
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), //左右也留安全边距
webView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor)//左右也留安全边距
])
2. H5 项目全局配置
文件: t1-h5/.umirc.ts
Viewport 配置:
metas: [
{
name: 'viewport',
content: 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover',
},
]
关键: viewport-fit=cover 确保页面延伸到屏幕边缘。
3. CSS 安全区域变量
文件: t1-h5/src/less/index.less
:root {
--safe-area-inset-top: constant(safe-area-inset-top);
--safe-area-inset-right: constant(safe-area-inset-right);
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
--safe-area-inset-left: constant(safe-area-inset-left);
// 支持新语法
--safe-area-inset-top: env(safe-area-inset-top);
--safe-area-inset-right: env(safe-area-inset-right);
--safe-area-inset-bottom: env(safe-area-inset-bottom);
--safe-area-inset-left: env(safe-area-inset-left);
}
4. 固定定位元素适配
对于使用 position: fixed 的底部元素,统一添加安全区域适配:
.fixedBottomElement {
position: fixed;
bottom: 0;
padding-bottom: calc(16px + constant(safe-area-inset-bottom));
padding-bottom: calc(16px + env(safe-area-inset-bottom));
/* 添加底部安全区域适配 */
// padding-bottom: env(safe-area-inset-bottom);
// padding-bottom: constant(safe-area-inset-bottom); /* iOS 11.0-11.2 兼容 */
}
安全区域适配( env(safe-area-inset-bottom) )只对以下情况有效:
- 固定定位元素 ( position: fixed 或 position: absolute 且贴底)
- 全屏容器 (如根容器)
工具类使用
文件: t1-h5/src/utils/safeArea.ts
import { useSafeArea } from '@/utils/safeArea';
// 在组件中使用
const { hasBottomInset, safeAreaInsets } = useSafeArea();
// 动态应用安全区域样式
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (elementRef.current) {
applySafeAreaStyles(elementRef.current, { bottom: true });
}
}, []);
适配原则
- iOS 原生层面: WebView 顶部适配安全区域,底部延伸到屏幕底部
- H5 全局层面: 使用 CSS 环境变量定义安全区域
- 组件层面: 固定定位的底部元素添加安全区域 padding
- 通用性: 提供工具类便于新组件快速适配
测试验证
在以下设备上验证效果:
- iPhone X/XS/XR 系列
- iPhone 11 系列
- iPhone 12/13/14/15 系列
- iPad Pro (Face ID)
注意事项
- 使用
constant()和env()双重声明确保兼容性 - 对于固定定位元素,使用
calc()函数叠加原有 padding - 避免在全局容器上直接添加安全区域 padding,应在具体组件上处理
- 横屏模式下左右安全区域也需要考虑适配
影响范围
✅ 已适配的页面/组件:
- 底部导航栏 (TabBar)
- 拍照结果页面 (PicResult)
- 分析结果页面 (AnalysisResult)
- 订正详情页面 (CorrectionDetail)
- 关于页面 (About)
- 评分页面 (Grading)
- 作业批改页面 (HomeworkGrading)
📝 需要继续关注的页面:
- 其他包含固定定位元素的页面
- 弹窗组件的安全区域适配
- 横屏页面的左右安全区域适配