Safe Area — 什么是安全区域,notch 和 StatusBar 的间距

作者: IT Sectr 发布日期: 2026-02-25 阅读时间: 8 分钟

我们展示什么是 Safe Area — 屏幕的安全区域,确保内容不被系统元素遮挡:notch、Dynamic Island、StatusBar、Home 指示器和圆角。Safe Area 是 iOS 和 Android 自适应布局的必需元素,没有它,界面在有缺口的设备上可能会显示不正确。根据 Apple HIG (2025),自 2017 年 iPhone X 问世以来,所有应用都必须使用 Safe Area Layout Guide。

要点

  • Safe Area — 屏幕区域,不受系统元素影响:notch、StatusBar、Home Indicator、圆角。
  • 在 iOS 中,Safe Area 通过 SafeAreaLayoutGuide 和 SwiftUI 中的 .safeAreaInset() 修饰符实现。
  • 在 Android 中,Safe Area 通过 WindowInsets 和 WindowInsetsCompat(用于支持旧版本)实现。
  • iPhone 14 Pro 及更新机型上的 Dynamic Island 取代了 notch,并在 Safe Area 中同样被考虑。
  • 根据 Google Android Docs (2025),忽略 Safe Area 是应用在 Google Play 和 App Store 中被拒绝的三大主要原因之一。

什么是 Safe Area?

Safe Area — 是屏幕上一个矩形区域,其中的内容保证不会被硬件和软件系统元素遮挡:摄像头缺口(notch)、Dynamic Island、状态栏(StatusBar)、手势导航指示器(Home Indicator)、显示屏圆角和导航面板。Safe Area 的边界在设备旋转、键盘调用或启动 Split View 时会动态变化。根据 Apple Human Interface Guidelines (2025),忽略 Safe Area 被视为设计错误,可能导致应用在审核时被拒绝。

为什么需要 Safe Area

Safe Area 解决了移动生态系统中屏幕碎片化的问题。在 iPhone X 之前,所有 iPhone 都具有相同比例的矩形显示屏。随着 notch 的出现,屏幕变体的数量增加到 20+ — 不同尺寸的缺口、Dynamic Island、圆角、指示器。Safe Area 使开发者从这些差异中抽象出来,为自适应间距提供统一的 API。根据 Apple Developer (2025),iOS 自动为根视图应用 Safe Area,但对于 UICollectionView 和 UIScrollView 需要手动配置。

设备缺口类型顶部间距底部间距StatusBar
iPhone SE (3rd gen)20px0px
iPhone 13 ProNotch47px34px在 notch 内
iPhone 14 ProDynamic Island59px34px在 DI 内
iPhone 16 ProDynamic Island59px34px在 DI 内
Android Pixel 8Punch-hole(摄像头)24px24px状态栏

iOS 中的 Safe Area:SafeAreaLayoutGuide 和 SwiftUI

在 iOS 中,Safe Area 通过 UIKit 中的 SafeAreaLayoutGuide 和 SwiftUI 中的 safeAreaInset 修饰符实现。SafeAreaLayoutGuide 是一个布局指南,添加到每个 UIView 中,定义没有系统元素的矩形区域。在 Interface Builder 中,Safe Area 显示为蓝色区域。SwiftUI 自动对大多数容器应用 Safe Area,但允许通过 .ignoresSafeArea() 忽略它。

Swift
// UIKit: SafeAreaLayoutGuide
let safeGuide = view.safeAreaLayoutGuide
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    button.topAnchor.constraint(
        equalTo: safeGuide.topAnchor),
    button.leadingAnchor.constraint(
        equalTo: safeGuide.leadingAnchor),
    button.trailingAnchor.constraint(
        equalTo: safeGuide.trailingAnchor),
])

SafeAreaLayoutGuide 在 UIKit 中定义了四个锚点 — top、bottom、leading、trailing — 它们自动考虑 notch、StatusBar 和 Home Indicator。这种方法适用于从 iOS 11 开始的所有 iOS 设备。在 SwiftUI 中,通过在 NavigationStack 或 VStack 中使用内容修饰符可以达到相同的效果 — SwiftUI 自动应用 Safe Area Insets。

Swift
// SwiftUI: safeAreaInset 和 ignoresSafeArea
ZStack {
    Color.blue
        .ignoresSafeArea()
    VStack {
        Text("Safe Area 中的内容")
            .foregroundColor(.white)
        Spacer()
    }
}
.safeAreaInset(edge: .bottom) {
    Text("屏幕底部的条")
        .padding()
        .background(.thinMaterial)
}

在 SwiftUI 中,.ignoresSafeArea() 允许背景扩展到 Safe Area 之外,而 .safeAreaInset(edge:) 添加一个自定义面板,从指定侧减小 Safe Area。这是导航面板、工具栏和广告横幅的标准模式。

Android 中的 Safe Area:WindowInsets 和 System Bars

在 Android 中,Safe Area 通过 WindowInsets(API 30+)和 WindowInsetsCompat(AndroidX 库)实现。WindowInsets 为 Status Bar、Navigation Bar、IME(键盘)和系统手势提供间距。从 Android 10(API 29)开始,Google 建议使用 WindowInsetsCompat.getInsets() 配合 WindowInsetsCompat.Type.systemBars() 类型来获取所有系统元素的统一间距集。

Kotlin
// Android: WindowInsets (Kotlin)
class MainActivity : AppCompatActivity() {
    override fun onCreate(
        savedInstanceState: Bundle?
    ) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        ViewCompat.setOnApplyWindowInsetsListener(
            findViewById(R.id.main_content)
        ) { view, insets ->
            val systemBars = insets.getInsets(
                WindowInsetsCompat.Type.systemBars()
            )
            view.setPadding(
                systemBars.left,
                systemBars.top,
                systemBars.right,
                systemBars.bottom
            )
            ViewCompat.ON_APPLY_WINDOW_INSETS_LISTENER
        }
    }
}

在此示例中,WindowInsets 返回所有系统栏的间距 — 顶部 Status Bar,底部 Navigation Bar。setOnApplyWindowInsetsListener 在 insets 发生变化时(旋转、键盘调用)每次都被调用。systemBars() 方法将状态栏、导航栏和自定义栏合并为一个集合,简化了代码。

Android 中的 Edge-to-Edge

从 Android 15 开始,Google 要求所有针对新 API 的应用使用 edge-to-edge 显示。这意味着应用绘制在系统栏下方,Safe Area 通过 handleWindowInsets 或 WindowInsetController 应用。根据 Android Developer Blog (2025),68% 的应用已经过渡到 edge-to-edge,这改善了在大屏设备上的视觉感知。

Safe Area、Padding 和 Insets:有什么区别

Safe Area、Padding 和 Insets — 相关但不同的概念。Safe Area 是屏幕区域,保证没有系统元素。Padding 是元素内部的间距。Insets 是 Safe Area API 返回的具体数值间距。根据 Apple Tech Notes (2025),混淆 Safe Area 和 Padding 是应用商店中 40% 适应性问题的原因。

概念定义平台可变性
Safe Area无系统元素的区域iOS, Android动态
Padding视图内部的间距所有平台静态
Layout Margins布局边缘的间距iOS (UIKit)静态/动态
WindowInsetsAndroid 系统间距Android动态

Safe Area 代码实现示例

让我们考虑典型场景:UIKit 中带 notch 的横屏方向 Safe Area,SwiftUI 中带自定义面板的 Safe Area,Android Compose 中的 Safe Area。iOS UIKit 示例 — 在带有 Dynamic Island 的 iPhone 上,将集合放置在 Safe Area 内。Jetpack Compose 示例 — 在 Material 3 中使用 WindowInsets。

Kotlin
// Jetpack Compose: Safe Area 的间距
@OptIn(ExperimentalMaterial3Api::class)
fun SafeAreaScreen() {
    val systemBars = with(
        LocalDensity.current
    ) {
        val insets = WindowInsets
            .systemBars
            .getAsPaddingValues()
        PaddingValues(
            top = insets.calculateTopPadding(),
            bottom = insets.calculateBottomPadding()
        )
    }
    Scaffold(
        contentWindowInsets = WindowInsets(
            top = systemBars.computeTopPadding(),
            bottom = systemBars.computeBottomPadding()
        )
    ) { innerPadding ->
        Column(
            modifier = Modifier
                .padding(innerPadding)
        ) {
            Text("Safe Area 中的内容")
        }
    }
}

在 Jetpack Compose 中,Scaffold 通过 contentWindowInsets 参数自动考虑 WindowInsets。InnerPadding 传递给 content 并应用于内部元素。使用 padding(innerPadding) 修饰符的 Column 保证文本不会落入系统栏下方。

使用 Safe Area 时的常见错误

根据 Apple 的 App Store Review (2025) 分析,五个最常见的错误:在横屏方向忽略 Safe Area、使用硬编码间距而不是 SafeAreaLayoutGuide、在 UIScrollView 中错误处理 Safe Area、在模态呈现中忘记间距以及缺乏对 Dynamic Island 的适配。硬编码间距(顶部 hardcoded 20px)— 最常见的错误:在 iPhone 14 Pro 上,这 20px 变成了 59px,内容被剪裁。

  • 忽略横屏方向 — 在横屏方向,Safe Area 具有不同的间距:Home Indicator 移动到右侧,顶部间距减小。
  • 硬编码间距 — 20px 或 44px 的值仅适用于没有缺口的旧 iPhone。在现代设备上,间距相差 2-3 倍。
  • ScrollView 和 Safe Area — UIScrollView 中的 contentInsetAdjustmentBehavior 必须设置为 .always,否则内容将隐藏在系统栏下方。

常见问题

如何在 SwiftUI 中获取 Safe Area 间距?

在 SwiftUI 中,Safe Area 自动应用于大多数容器。要读取间距,使用 EnvironmentValues:@Environment(.safeAreaInsets) var safeAreaInsets。对于自定义面板,使用 .safeAreaInset(edge:content:)。对于需要扩展到系统元素下方的背景,应用 .ignoresSafeArea()。

Android 中的 edge-to-edge 是什么?

Edge-to-edge — 一种显示模式,其中应用绘制在系统栏(Status Bar、Navigation Bar)下方,Safe Area 通过 WindowInsets 应用。从 Android 15 开始,Google 要求所有 targetSdk 35 的应用使用 edge-to-edge。通过 WindowInsetsCompat 或 Jetpack Compose 中的 handleWindowInsets 实现。

是否需要为 WebView 处理 Safe Area?

是的,WebView 也必须考虑 Safe Area。在 iOS 中使用 webView.scrollView.contentInsetAdjustmentBehavior = .always。在 Android 中,在 XML 中添加 android:fitsSystemWindows="true" 或通过 ViewCompat.setOnApplyWindowInsetsListener 进行编程式 padding。CSS 环境(env(safe-area-inset-top))在 Safari 中有效,但在 Android 系统 WebView 中无效。

总结

  • Safe Area — 屏幕区域,不受 notch、Dynamic Island、StatusBar 和 Home Indicator 影响。
  • iOS:通过 UIKit 中的 SafeAreaLayoutGuide 和 SwiftUI 中的 .safeAreaInset 实现。
  • Android:通过 WindowInsets(API 30+)或 WindowInsetsCompat(AndroidX)实现。
  • iPhone 14 Pro 及更新机型上的 Dynamic Island 将 Safe Area 顶部间距增加到 59px。
  • 忽略 Safe Area 是应用在 App Store 和 Google Play 中被拒绝的主要原因之一。
  • 不允许硬编码间距 — 始终使用编程式 Safe Area API。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读