ios开发问题-安全边距

305 阅读3分钟

iPhone Safe Area 安全区域适配解决方案

问题描述

iPhone X 系列及更新设备引入了"刘海"和底部"Home Indicator",导致网页内容可能被遮挡或与底部留有过大间距。

image.png

解决方案概述

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) )只对以下情况有效:

  1. 固定定位元素 ( position: fixed 或 position: absolute 且贴底)
  2. 全屏容器 (如根容器)

工具类使用

文件: 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 });
  }
}, []);

适配原则

  1. iOS 原生层面: WebView 顶部适配安全区域,底部延伸到屏幕底部
  2. H5 全局层面: 使用 CSS 环境变量定义安全区域
  3. 组件层面: 固定定位的底部元素添加安全区域 padding
  4. 通用性: 提供工具类便于新组件快速适配

测试验证

在以下设备上验证效果:

  • iPhone X/XS/XR 系列
  • iPhone 11 系列
  • iPhone 12/13/14/15 系列
  • iPad Pro (Face ID)

注意事项

  1. 使用 constant()env() 双重声明确保兼容性
  2. 对于固定定位元素,使用 calc() 函数叠加原有 padding
  3. 避免在全局容器上直接添加安全区域 padding,应在具体组件上处理
  4. 横屏模式下左右安全区域也需要考虑适配

影响范围

✅ 已适配的页面/组件:

  • 底部导航栏 (TabBar)
  • 拍照结果页面 (PicResult)
  • 分析结果页面 (AnalysisResult)
  • 订正详情页面 (CorrectionDetail)
  • 关于页面 (About)
  • 评分页面 (Grading)
  • 作业批改页面 (HomeworkGrading)

📝 需要继续关注的页面:

  • 其他包含固定定位元素的页面
  • 弹窗组件的安全区域适配
  • 横屏页面的左右安全区域适配