Alert 警告提示弹窗

Alert 是一种强制回应视图。当它出现时,整个画面会被锁住。如果你不点击上面的任何一个选项,你无法做接下来的动作。


参数:弹窗标题

// 参数弹窗标题,字符串
.alert("Important message", isPresented: $showingAlert) {
		Button("OK") { }
}

参数:弹窗按钮闭包

// 参数,是弹窗按钮视图的闭包
.alert("Important message", isPresented: $showingAlert) {
		Button("OK") { }
		// Button 后面如果是空的闭包,这意味着没有设置任何在按下按钮时运行的功能
		// 这不重要,因为点击 Alert 中的任何按钮都会自动关闭 Alert 弹窗,不需要自己设置
		// 如果有任何额外的功能需要添加,就加到 button 的闭包中
}

// Alert 弹窗最多可以添加 2 个按钮,不能再多
// 并且最好添加按钮的角色,以确保每个按钮的作用一目了然
.alert("Important message", isPresented: $showingAlert) {
    Button("Delete", role: .destructive) { }
    Button("Cancel", role: .cancel) { }
}

参数:消息文本 message

// 还可以在窗口内添加消息文本,以在标题旁边加上第二个尾随尾部 message,如下所示:
Button("Show Alert") {
    showingAlert = true
}
.alert("Important message", isPresented: $showingAlert) {
    Button("OK", role: .cancel) { }
} message: {
    Text("Please read this.")
}
// 两个闭包一起设置
.alert( ... )

//第1个闭包:设置按钮
{
		//除了上面一个例子的设置方式,还可以让系统提供默认的“确定”按钮,_ in代表不需要任何按钮
		_ in
}
//第2个闭包:设置说明文字
message: {
		facility in
		Text(facility.description)
}

参数:isPresented 绑定 Bool

// 使用 isPresent 双向数据绑定状态属性的布尔值。
// 绑定后,程序将监视该布尔值 ,一旦它为真就会显示弹窗;
.alert("Important message", isPresented: $showingAlert) {
		Button("OK") { }
}

// 为什么要双向绑定?因为当弹窗被关闭时,布尔值会自动设回 false

参数:presenting 绑定可选值

使用 presenting 绑定可选值,其关键是使用 optional 的、符合 Identifiable 协议的对象作为显示 alert 的条件,并且闭包提供了用于该条件的非可选值,因此可以安全地使用它。

// 1. 首先创建一个符合 Identifiable 协议的简单 User 结构
struct User: Identifiable {
    var id = "Taylor Swift"
}

// 2. 然在 ContentView 内创建一个属性来跟踪选择的用户,默认设置为 nil
@State private var selectedUser: User? = nil
@State private var isShowingUser = false

// 3. 需要同时传递【布尔值】和【可选值】,这样可以在需要时显示弹窗,也可以通过可选值进行解包
Text("Hello, World!")
		.onTapGesture{
				// 如果只保留布尔值这一行代码,也是能呈现 alert 的,只是获取不到可选值的数据了
				isShowingUser = true
				// 如果只保留可选值赋值这一句代码,无法呈现 alert
				selectedUser = User()
		}
		.alert(
				"Welcome",
				isPresented: $isShowingUser,
				presenting: selectedUser
		){
				//成功获取解包后的 user
				user in
				Button(user.id){}
		}
// 加上 Message 参数的写法
.alert("Delete Project", isPresented: $reconfirmDialog, presenting: projectToDelete, actions: { project in
		Button("Delete", role: .destructive) { removeProject(project) }
    Button("Cancel", role: .cancel) { }
}, message: { _ in
    Text("该项目所包含的所有点子也将被删除。")
})

<aside> 💡 注意:用 Alert 绑定 optional 值的时候,必须同时传递: 控制视图展示的【布尔值】,以及 遵循 Identifiable 协议的 【可选值】。这点和 sheet 视图不一样,参见 参数:item 绑定 Optional

</aside>


Alert 旧版声明方法

//方式1
.alert(isPresented: $showAlert) { 
	() ->
	Alert in
	return Alert(
		// 标题
		title: Text("Hello there!"),
		// 说明
		message: Text("this is my first pop up"),
		// 解除按钮样式
		dismissButton: .default(Text("Awesome"))
	)
}

//方式2
.alert(
		isPresented: $showAlert, 
		content: {
				Alert(
				    title: Text("Error"),
		        message: Text(self.alertMessage),
		        dismissButton: .default(Text("OK"))
				)
		}
)
//省略掉闭包的参数
.alert(isPresented: $showAlert) {
    Alert(
			title: Text("..."), 
			message: Text(alertMessage), 
			primaryButton: .default(Text("Yes"), 
					action: { 
						self.presentationMode.wrappedValue.dismiss() }
					), 
			secondaryButton: .cancel(Text("No"))
		)
}
.alert("Welcome", isPresented: $isShowingUser) { }
//当它运行时,你会看到一些有趣的东西:尽管没有明确指定“确定”按钮,但结果与以前完全相同
//SwiftUI 发现警报中没有任何操作,因此它添加了一个默认操作,标题为“OK”,并且在点击时会关闭警报

在视图中显示多个警报

如果您尝试将多个 alert() 修饰符附加到单个视图,您会发现您的代码无法按预期工作(一个警报可以工作,但另一个则不行)。

要解决此问题,您需要确保为每个视图附加不超过一个 alert() 修饰符。记住:您不需要将警报附加到同一视图,您可以将它们附加到任何地方。事实上,您会发现将它们直接附加到显示它们的东西(例如按钮)最适合。


confirmationDialog 确认对话框

SwiftUI 使用 confirmationDialog() 修饰符向用户显示一系列选项。之前的 ActionSheet 在 iOS14 已被 confirmationDialog 代替。如果您的目标是 iOS 14 之前版本,则需要使用 ActionSheet 。但如果目标是 iOS 15 版本,或者想支持 macOS,则应该使用 confirmationDialog() 。

SwiftUI 提供了 alert() 来呈现重要的选择;提供了 sheet() 来在当前视图之上呈现整个视图;它也提供了 confirmationDialog() ,一种可以添加许多按钮的替代方案 。它是一个从屏幕底部向上滑动的按钮列表,您可以添加任意数量的按钮,它甚至可以滚动。

从创建方式上看,confirmationDialogAlert 几乎相同:

从视觉上看,confirmationDialogAlert 非常不同:


参数:标题&消息

.confirmationDialog("Change background", isPresented: $showingConfirmation) { ... }

// 标题显示或隐藏
.confirmationDialog("Change background", isPresented: $showingConfirmation, titleVisibility: .visible) { ... }

参数:消息闭包

.confirmationDialog("Change background", isPresented: $showingConfirmation) {
	...
} message: {
    Text("Select a new color")
}

//除了提供一个标题,还可以选择附加一条消息。按钮按照提供的顺序垂直堆叠在屏幕上,通常最好在末尾添加一个取消按钮。虽然可以通过点击屏幕上的其他位置来取消,但最好为用户提供明确的说明选项。

参数:isPresented 绑定 Bool

@State private var showingConfirmation = false

// 绑定状态属性:决定对话框当前是否显示
.confirmationDialog("Change background", isPresented: $showingConfirmation) { ... }

参数:按钮组视图的闭包

// 按钮是按照提供的顺序垂直堆叠在屏幕上,通常最好在末尾添加一个取消按钮
// 虽然可以通过点击屏幕上的其他位置来取消,但最好为用户提供明确的说明选项

.confirmationDialog("Change background", isPresented: $showingConfirmation) {
    Button("Red") { backgroundColor = .red }
    Button("Green") { backgroundColor = .green }
    Button("Blue") { backgroundColor = .blue }
    Button("Cancel", role: .cancel) { }
} message: {
    Text("Select a new color")
}

完整例子:

@State private var showingConfirmation = false

.confirmationDialog("Change background", isPresented: $showingConfirmation) {
    Button("Red") { backgroundColor = .red }
    Button("Green") { backgroundColor = .green }
    Button("Blue") { backgroundColor = .blue }
    Button("Cancel", role: .cancel) { }
} message: {
    Text("Select a new color")
}

// 因为这个新的 API 更加灵活,实际上可以使用 ForEach 将这些操作折叠成一个简单的循环:
struct ContentView: View {
    @State private var showingOptions = false
    @State private var selection = "None"

    var body: some View {
        VStack {
            Text(selection)

            Button("Confirm paint color") {
                showingOptions = true
            }
            .confirmationDialog("Select a color", isPresented: $showingOptions, titleVisibility: .visible) {
                ForEach(["Red", "Green", "Blue"], id: \\.self) { color in
                    Button(color) {
                        selection = color
                    }
                }
            }
        }
    }
}