
移动端没有桌面的窗口机制,各种弹窗都是系统功能。
目录
[.sheet 弹窗](#.sheet 弹窗)
[.fullScreenCover 全屏弹窗](#.fullScreenCover 全屏弹窗)
[.actionSheet 选项弹窗](#.actionSheet 选项弹窗)
[.alert 告警](#.alert 告警)
[.contextMenu 上下文菜单](#.contextMenu 上下文菜单)
.sheet 弹窗
sheet是一种弹窗机制,目前的默认实现是从屏幕下端升起,覆盖屏幕的绝大部分。
sheet属于整个屏幕,虽然把.sheet代码放在最外层符合逻辑,但实际上放在哪一层都没有关系,效果都是一样的。
基本的sheet用法:
Swift
@State var b = false
@State var str = "info"
var body: some View {
Form {
Button {b = true}label: {
Text("打开sheet")
}
Text(str)
}
.sheet(isPresented: $b) {
Text("关闭")
.onTapGesture {
str = str + "+1"
b = false
}
}
}
sheet需要一个变量来指示是否显示,需要关闭的时候设置变量为false即可。很明显,sheet是当前页面的一部分,只是根据指示显示出来。
初始:

点一下"打开sheet":

sheet打开,占据屏幕绝大部分。再点一下"关闭":

"info"变成了"info+1",符合预期。
由于界面和数据完全通过绑定变量交互,不用太关心sheet对象再次打开时是重新创建还是显示原来的对象,不像桌面程序,要考虑对象的创建和显示/隐藏。
.fullScreenCover 全屏弹窗
只需要简单地把.sheet替换成.FullScreenCover即可,效果:

除了全屏化没别的区别。
.actionSheet 选项弹窗
注意,已经被标记为过时,推荐用confirmationDialog代替。
ActionSheet提供类似菜单的操作,显示方式和前面的两种一样,用一个变量来表示是否显示,选项弹窗的关键代码如下:
Swift
.actionSheet(isPresented: $b) {
return ActionSheet(
title: Text("操作"),
message: Text("随便选一个"),
buttons: [
.cancel(
Text("取消"),
action: { str = str + "+cancel" }),
.destructive(
Text("危险 动作1"),
action: { str = str + "+destructive" }
),
.default(
Text("默认 动作2"),
action: { str = str + "+default" }
),
]
)
}
效果:

上面的代码在处理动作的时候并没有重置绑定变量b,设置的取消按钮也没有显示,但其实只需要点击弹窗以外的区域弹窗就会关闭,而且,任何一个动作结束都会自动重置绑定变量。
点击空白处取消:

再弹出来一次,点击一个选项:

动作都是正确执行的。
我们创建按钮的时候用了.destructive和.default,这两个是按钮的"角色",前一个表示是破坏性的,所以系统自动使用红色显示,后一个是默认,系统使用黑色显示。
.alert 告警
写法跟actionSheet差不多:
Swift
.alert(isPresented: $bAlert) {
return Alert(title: Text("告警")
,message: Text("确定吗?")
,primaryButton: .destructive(Text("确认")){str = str + "+ok"}
,secondaryButton: .default(Text("取消")){str = str + "+canecl"}
)
}
效果:

说实话这东西真没有太多自定义的必要,但是从交互设计角度来说,文字应该居中显示。
.contextMenu 上下文菜单
上下文菜单需要通过长按呼出。跟前面的几个不同,不需要变量来指示是否显示,而且显示位置和修饰的对象有关。
下面的代码分别给整体和一个单独的Text设置了上下文菜单:
Swift
var body: some View {
Form {
//。。。。。。
Text(str)
.contextMenu {
Button("上下文菜单3") {
str = str + "+contextMenu3"
}
Button("上下文菜单4") {
str = str + "+contextMenu4"
}
}
}
//。。。。。。
.contextMenu {
Button("上下文菜单1") {
str = str + "+contextMenu1"
}
Button("上下文菜单2") {
str = str + "+contextMenu2"
}
}
}
长按Text的效果:

长按其余部分的效果:

汇总代码
本文的完整代码:
Swift
import SwiftUI
struct SwiftUIViewPopup: View {
@State var bSheet = false
@State var bFullScreenCover = false
@State var bActionSheet = false
@State var bAlert = false
@State var str = "info"
var body: some View {
Form {
Button {
bSheet = true
} label: {
Text("打开sheet")
}
Button {
bFullScreenCover = true
} label: {
Text("打开FullScreenCover")
}
Button {
bActionSheet = true
} label: {
Text("打开ActionSheet")
}
Button {
bAlert = true
} label: {
Text("打开Alert")
}
Text(str)
.contextMenu {
Button("上下文菜单3") {
str = str + "+contextMenu3"
}
Button("上下文菜单4") {
str = str + "+contextMenu4"
}
}
}
.sheet(isPresented: $bSheet) {
Text("关闭")
.onTapGesture {
str = str + "+1"
bSheet = false
}
}
.fullScreenCover(isPresented: $bFullScreenCover) {
Text("关闭")
.onTapGesture {
str = str + "+a"
bFullScreenCover = false
}
}
.actionSheet(isPresented: $bActionSheet) {
return ActionSheet(
title: Text("操作"),
message: Text("随便选一个"),
buttons: [
.cancel(
Text("取消"),
action: { str = str + "+cancel" }
),
.destructive(
Text("危险 动作1"),
action: { str = str + "+destructive" }
),
.default(
Text("默认 动作2"),
action: { str = str + "+default" }
),
]
)
}
.alert(isPresented: $bAlert) {
return Alert(
title: Text("告警"),
message: Text("确定吗?"),
primaryButton: .destructive(Text("确认")) { str = str + "+ok" },
secondaryButton: .default(Text("取消")) { str = str + "+canecl" }
)
}
.contextMenu {
Button("上下文菜单1") {
str = str + "+contextMenu1"
}
Button("上下文菜单2") {
str = str + "+contextMenu2"
}
}
}
}
#Preview {
SwiftUIViewPopup()
}