js
override func viewDidLayoutSubviews() {
webView.frame = self.view.bounds
}
关键发现 :iOS项目中WebView使用的是 self.view.bounds ,这意味着WebView占据了整个视图控制器的边界, 包括安全区域 。
H5项目中的安全区域处理
js
.app {
height: 100vh;
display: flex;
flex-direction: column;
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.bottom {
flex: 0;
border-top: solid 1px var(--adm-color-border);
background-color: #fff;
position: relative;
z-index: 1;
/* 添加底部安全区域适配 */
padding-bottom: env(safe-area-inset-bottom);
padding-bottom: constant(safe-area-inset-bottom);
}
问题根源
底部留有距离的原因是 双重安全区域处理 :
- iOS层面 :WebView已经占据了包含安全区域的完整视图边界
- H5层面 :CSS又额外添加了安全区域的padding 这导致在有Home Indicator的iPhone设备上,底部会有双倍的安全区域间距。
iPhone Safe Area 安全区域适配解决方案
问题描述
iPhone X 系列及更新设备引入了"刘海"和底部"Home Indicator",导致网页内容可能被遮挡或与底部留有过大间距。
解决方案概述
1. iOS 原生项目修改
文件 : t-ios/t/ViewController.swift
修改内容:
- 使用 Auto Layout 约束替代手动设置 frame
- WebView 顶部适配到 safeAreaLayoutGuide
- 底部延伸到屏幕底部,由 H5 页面处理安全区域
swift
// 设置 WebView 约束,确保适配安全区域
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
2. H5 项目全局配置
文件 : t1-h5/.umirc.ts
Viewport 配置:
javascript
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
css
: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. 布局组件适配
文件 : t1-h5/src/layouts/layout.less
css
.bottom {
// 为底部 TabBar 添加安全区域适配
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
5. 固定定位元素适配
对于使用 position: fixed
的底部元素,统一添加安全区域适配:
css
.fixedBottomElement {
position: fixed;
bottom: 0;
padding-bottom: calc(16px + constant(safe-area-inset-bottom));
padding-bottom: calc(16px + env(safe-area-inset-bottom));
}
工具类使用
文件 : t1-h5/src/utils/safeArea.ts
typescript
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)
📝 需要继续关注的页面:
- 其他包含固定定位元素的页面
- 弹窗组件的安全区域适配
- 横屏页面的左右安全区域适配