SwiftUI 精通之路 12: SwiftUI 中的安全区域 SafeArea

1,378 阅读7分钟

前言

CleanShot 2024-10-26 at 09.38.33@2x.png

月上柳梢头,人约黄昏后

正文

进入正文,我们第一步还是首先创建我们的工程文件,我们打开我们创建的项目添加新的文件。Xcode 顶部的 File > New > File form Template... 快捷打开创建模板面板的的快捷键是 ⌘ + N.

我们创建名称为 SafeAreaBootcamp.swift 的文件.

CleanShot 2024-10-26 at 15.47.34@2x.png

在开始案例之前,我们先普及下 SwiftUISafe Area 的概念:

safeArea 区域指的是当前设备(iPad, iOS, MacOS) 屏幕上可以安全放置内容,不会被遮挡的区域,也就是在设备中除了 屏幕顶部的状态栏区域底部的指示区域,或者刘海屏的凹口区域等,因为这些区域都有可能遮挡住内容的正常显现.

本文我们主要学习如何在 SwiftUI 中使用 safearea 区域,如何在安全区域外绘制我们的视图.

首先我们先看看目前屏幕的安全区域,并且我们尝试使用几何阅读器 GeometryReader 这个视图构造器去帮助我们获取当前容器的边距.

GeometryReader { geometry in
    let safeAreaInsets = geometry.safeAreaInsets
    Text("Top inset: \(safeAreaInsets.top), \nBottom inset: \(safeAreaInsets.bottom)")
}
CleanShot 2024-10-26 at 16.12.28@2x.png

在上述代码中,我们通过 GeometryReader 来访问当前设备屏幕的 safeareaInsets,可以获取当前视图的安全区域的内边距,包括顶部(状态栏高度)和底部(主屏幕指示器高度)的距离值。

注意:safeareaInsets 主要是用来表示确定视图安全区域的插入值对象,如果一个视图可以完整的放置在父视图容器的安全区域内,那么当前视图的 safeareaInsets 就是为 0. 可以看下面示例:

CleanShot 2024-10-26 at 16.25.50@2x.png

我们现在已经知道如何获取安全区域插值集合,使用 GeometryReader 可以很简单的获取,那么 我现在遇到一个问题:

我想设置我应用全屏的背景设置为黑色.black,目前我这样设置,代码如下:

CleanShot 2024-10-26 at 16.32.02@2x.png

你可以看到颜色没有显示在安全区域外,仅仅是设置黑色背景到安全区域内。那么问题来了!

在 SwiftUI 中想要突破安全区域的限制,将视图或者修饰器效果应用到安全区域外,我们可以借助 ignoresSafeArea 修饰器很简单解决!包括顶部的状态栏、底部的 Home 指示条,以及键盘等影响布局的部分。

CleanShot 2024-10-26 at 16.36.54@2x.png

在默认情况下,.ignoreSafeArea()默认参数是全部方向以及忽略全部的安全区域划分,我们也可以传入特定参数进行指定.

.ignoreSafeArea()接受两个参数:

  1. regions 安全区域(.all, .container, .keyboard)

.all(默认参数) 指.container, .keyboard 两种安全区域划分的集合

.container

主要是当前设备以及当前用户界面容器定于的安全区域,这里包括容器的顶部导航栏区域以及底部的状态栏指示区域。

常见使用场景:例如在全屏显示图片或视频时,使用 .container 可以让视图占满整个屏幕,不被系统的安全区域限制。

.keyboard

忽略键盘显示时的安全区域,当使用.keyboard 时,视图会忽略键盘弹窗时给出的安全区域,这样视图可以延伸到视图后面。

常见使用场景:当你需要在键盘弹出时,内容不自动上移(例如设计一个全屏背景,但希望键盘弹出时背景不被裁剪)。

  1. edges 指定方向(.all, .vertical, .horizontal, .top, .bottom, .leading, .trailing)
// 只扩展到底部
.ignoresSafeArea(edges: .bottom)

// 扩展到顶部和底部
.ignoresSafeArea(edges: [.bottom, .trailing])

// 横向扩展
.ignoresSafeArea(edges:.horizontal)

通常我都是使用默认参数,或者大部分指定忽略安全区域的方向,例如下面这样:

CleanShot 2024-10-26 at 17.14.15@2x.png

但是后面你读一些资深开发的博客以及再次看这个 API 的时候发现,原来这个参数还有这个作用!

是的,我们后面简单提下.ignoreSafeArea()regions: .keyboard 这个参数主要解决的是哪一块的问题,你可能看上面界面自己也大概知道了解决什么问题,那么你很有天赋,其实我当初学习的时候还真是看案例后才知道!

从字面量上我们肯定知道这个肯定跟输入键盘有关,我们首先定义文本输入框视图:

ZStack {
    Color.black.ignoresSafeArea()

    TextField("输入内容", text: .constant(""))
        .textFieldStyle(.plain)
        .padding()
        .background(Color.white)
}
CleanShot 2024-10-26 at 17.24.25@2x.png

我们下一步是需要在模拟器上去运行我们的项目,我们将当前文件名称复制到我们当前项目的主入口文件那里进行替换我们的视图

CleanShot 2024-10-26 at 17.25.39@2x.png

后面我们点击顶部的三角形符号运行!

CleanShot 2024-10-26 at 17.31.28.gif

很显然,目前效果是如果我们点击文本输入框,弹出的键盘会顶住内容向上进行偏移,从而会导致页面视图会进行重新布局调整,其实这种效果说好也好说好也不好,看需求而定,但是如果不需要这种效果怎么办呢,那么就需要.keyboard 这个属性帮助!

注意⚠️:有时候如果你打开模拟器,但是点击文本框没有出现键盘,那么你这时候使用的是系统的键盘,如果需要显示键盘你可以下面设置!

CleanShot 2024-10-26 at 17.36.52.gif

快捷键 ⌘ + K

我们现在看看应用后的效果,我们添加 .ignoresSafeArea(.keyboard)

CleanShot 2024-10-26 at 17.41.36.gif

现在我们已经知道什么是 safearea 以及 知道如何去进行忽略 SwiftUI 中的安全边界的限制,那么如果我们控制安全区域的大小以及如何自定义添加设置安全区域内的视图元素,这就是我们 safeAreaInset 帮助我们完成的部分!

safeAreaInset 是 iOS15 开始引入的,主要就是方便开发者在视图的顶部、底部、左侧或右侧的安全区域中插入内容,而不影响视图的主要布局。它非常适用于添加导航栏、工具栏或消息提示等内容。

VStack {
    TextField("输入内容", text: .constant(""))
        .textFieldStyle(.plain)
        .padding()
        .background(Color.white)
    Text("主内容区域")
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(Color.blue)
}
.safeAreaInset(edge: .bottom) {
    HStack {
        Button("按钮 1") { /* 执行操作 */ }
        Spacer()
        Button("按钮 2") { /* 执行操作 */ }
    }
    .padding()
    .frame(maxWidth: .infinity)
    .background(Color.red.opacity(0.8))
}
CleanShot 2024-10-26 at 17.56.45@2x.png CleanShot 2024-10-26 at 18.02.49.gif 在这个示例中,底部插入了一个 HStack 工具栏,包含两个按钮。当视图延伸至底部时,工具栏会出现在安全区域内,并且不会覆盖到主内容。

我们最后看看这个修饰器的参数

.safeAreaInset(edge: .top, spacing: 0) {
    // 在顶部安全区域插入的内容
}

edge:插入内容的位置,可以是 .top、.bottom、.leading 或 .trailing,分别表示顶部、底部、左侧或右侧的安全区域。

spacing:视图和安全区域之间的间距,默认为 0。

content:插入的内容视图,可以是任何 SwiftUI 视图。

好的,关于安全区域的的部分就到这里,安全区域主要就是 SwiftUI 中为了正常显示内容而根据不同的设备而有的参数,我们可以根据这个去配置我们的用户界面如何显示,显示在安全区域内还是显示安全区域外。

目前主要使用比较多的是背景设置这块,希望设置全屏显示的背景,所以忽略安全区域限制设置全屏背景显示,借助 safeAreaInset 我们可以制作类似微信输入的效果,当键盘弹出的时候输入框也跟随键盘位置弹出但是不会影响当前页面的布局。这个其实就是借助 safeAreaInset 去进行实现,在后续中我也会在实战案例分享中进行分享!

那么再见,下章再见!一起进步!