banner
NEWS LETTER

swiftUI中生成弹窗警告的一些理解

Scroll down

# Swift

力荐一个学习swift/swiftUI的网站:Hacking with Swift
(注:本文章中的部分idea来自于Tiny66)

iOS的警告弹框

在学习过程中,准确说是在Day31左右时,写的一个小app:WordScramble 中,我们需要写几个需要警告弹框的地方。然后我就发现,swiftUI中实现警告弹框的逻辑还挺有特点 —— 怎么说呢,个人感觉并不简洁…但是!还是很有逻辑的,所以在这里分享一下我的学习心得。

  • iOS中的警告弹框,就是如下的形式:
    warning

实现一个简单的警告弹框

首先,我们要声明一个 @State 的布尔值变量 showingAlert,默认应为false,在我们想要弹出警告时,将其变为true。(因为我们在调用 .alert(isPresented: $showingAlert)时,需要布尔值来判断当前情况是否需要弹出警告。

  • 因此,最基础的实现如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import SwiftUI

struct ContentView: View {
// 1
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
// 2
showingAlert.toggle() // 反转布尔值
}
.alert(isPresented: $showingAlert) { // 3
Alert( // 4
title: Text("Title"), // 标题
message: Text("Message"), // 消息内容
dismissButton: .cancel() // 取消按钮
)
}
}
}
  • 需要注意的是,在这个版本中,我们使用了Alert结构体来创建警告弹框。在iOS 15以后的版本中,我们也可以用如下的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import SwiftUI

struct ContentView: View {
// 1
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
// 2
showingAlert.toggle()
}
.alert(Text("Title"), isPresented: $showingAlert) { // 3
Button("Cancel", role: .cancel) { }
} message: {
Text("Message")
}
}
}

其中,注释 1 和 2 部分的代码相同,注释 3 的代码不同,区别在于在iOS 15以后,我们不再需要使用Alert结构体,直接把按钮,标题,信息传递给.alert即可。
另外需要注意的是,在我们点击alert中的按钮后,showingAlert会自动切换回false,不需要手动进行。方便了下次调用,同时也是为什么警告弹框会在点击按钮后消失的原因。

实现的效果如下:
basic

显示两个按钮

在理解基本的思路和原理后,如果想要实现警告弹框中有两个按钮,甚至多个按钮,就十分简单了:

  • 在旧方法中实现两个按钮:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import SwiftUI

struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert.toggle()
}
.alert(isPresented: $showingAlert) {
Alert(
title: Text("Title"),
message: Text("Message"),
primaryButton: .default(Text("Confirm")), // 1
secondaryButton: .cancel() // 2
)
}
}
}

与上面的区别就是,我们在这里使用了primaryButtonsecondaryButton,用来显示两个按钮;而里面的.default()Alert.Button.default()的缩写,.cancelAlert.Button.cancel()的缩写。

此外,Alert.Button还有另外一种样式:.destructive,生成一个红色字体的按钮,常用于删除等操作时的提示。

  • 在iOS 15以后的版本中,我们则可以这么写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SwiftUI

struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert.toggle()
}
.alert(Text("Title"), isPresented: $showingAlert) {
Button("Cancel", role: .cancel) { } // 1
Button("Confirm") { } // 2
} message: {
Text("Message")
}
}
}

这样写的话,相比于一个按钮的版本,仅仅是增加了一行按钮而已,非常的简洁。

而如果要指定样式,则使用role,和旧版本一样也是三种:

  1. .default,不需要指定role,显示蓝色文本;
  2. .cancel,role的值为.cancel,显示加粗的蓝色文本;
  3. .destructive,role的值为.destructive,显示红色文本。

实现的效果如下:
twoButton

  • 值得一提的是,如果使用iOS 15以后的这个版本,我们可以往其中添加任意多个按钮,不受限制。例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import SwiftUI

struct ContentView: View {
@State private var showingAlert = false
var body: some View {
Button("Show Alert") {
showingAlert.toggle()
}
.alert(Text("Title"), isPresented: $showingAlert) {
Button("Cancel", role: .cancel) { }
Button("button 1") {}
Button("button 2") {}
Button("button 3", role: .destructive) {}
Button("button 4", role: .destructive) {}
} message: {
Text("Message")
}
}
}

实现的效果如下:
multi

显示不同文案的警告弹框

  • 如果页面上有不同的几个按钮,点了之后要显示不同文案的弹框,可以这样做:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//旧版本

import SwiftUI
// 1
struct AlertMessage: Identifiable {
var id: String {message}
var title: String
var message: String
}

struct ContentView: View {
// 2
@State private var alertMessage: AlertMessage?
var body: some View {
// 3
VStack {
Button("Show Alert1") {
alertMessage = AlertMessage(title: "Alert 1", message: "message 1")
}
Button("Show Alert2") {
alertMessage = AlertMessage(title: "Alert 2", message: "message 2")
}
}
// 4
.alert(item: $alertMessage) {message in
Alert(
title: Text(message.title), // 5
message: Text(message.message),
primaryButton: Alert.Button.default(Text("Confirm")),
secondaryButton: Alert.Button.cancel()
)
}
}
}

其中:

  1. 定一个AlertMessage结构体,用来表示消息,因为要使用在.alert(item:content)方法中,所以必须遵循Identifiable协议;
  2. 定义一个optional的状态变量alertMessage,用来存放消息,程序运行时,如果计算出此变量为nil,alert不显示,否则显示;
  3. 添加多个按钮,点击的时候将alertMessage设置成不同的值;
  4. 调用.alert(item:content)方法,绑定alertMessage
  5. 显示不同的message。
  • 在iOS 15以后的版本,我们这样写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import SwiftUI

struct AlertMessage: Identifiable {
var id: String {message}
var title: String
var message: String
}

struct ContentView: View {
// 1
@State private var alertMessage: AlertMessage?
// 2
@State private var $showingAlert = false
var body: some View {
VStack {
Button("Show Alert1") {
alertMessage = AlertMessage(title: "Alert 1", message: "message 1")
// 3
$showingAlert = true
}
Button("Show Alert2") {
alertMessage = AlertMessage(title: "Alert 2", message: "message 2")
$showingAlert = true
}
}
.alert(alertMessage?.title ?? "",
isPresented: $$showingAlert,
presenting: alertMessage // 4
) { message in
Button("Cancel", role: .cancel){}
Button("Confirm") {}
} message: { message in
Text(message.message)
}
}
}

iOS15 alert还是需要绑定一个布尔值,所以我们还是定义了一个showingAlert,点击按钮的时候将其值设为true,用来显示alert。

另外定义了一个alertMessage变量存放消息,点击按钮是设置成不同的值。

调用.alert(_:isPresented:presenting:actions:message)方法,将alertMessage作为参数传入,最后在message里获取具体的消息内容。

使用警告弹框的实例

这是我在学习中的 WordScramble app中的应用实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import SwiftUI

struct ContentView: View {
@State private var usedWords = [String]()
@State private var rootWord = ""
@State private var newWord = ""

@State private var errorTitle = ""
@State private var errorMessage = ""
@State private var showingError = false

@State private var score = 0

var body: some View {
NavigationStack {
List {
Section {
TextField("Enter your word", text: $newWord)
.textInputAutocapitalization(.never)
Text("Your score: \(score)")
}

Section {
ForEach(usedWords, id: \.self) { word in
HStack {
Image(systemName: "\(word.count).circle")
Text(word)
}
}
}
}
.navigationTitle(rootWord)
.onSubmit(addNewWord)
.toolbar{
Button("Start!", action: startGame)
}
.alert(errorTitle, isPresented: $showingError) { } message: {
Text(errorMessage)
}
}
}

func addNewWord() {
let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines)

guard answer.count > 0 else { return }

guard isOriginal(word: answer) else {
wordError(title: "Word used already", message: "Be more original!")
return
}

guard isPossible(word: answer) else {
wordError(title: "Word not possible", message: "You can't spell that word from '\(rootWord)'!")
return
}

guard isReal(word: answer) else {
wordError(title: "Word not recognized", message: "You can't just make them up, you know!")
return
}

guard isOK(newWord: answer) else {
wordError(title: "Word not legal", message: "You can't enter a word less than 3 or equal!")
return
}

withAnimation {
usedWords.insert(answer, at: 0)
}

score += 1

newWord = ""
}

func startGame() {
if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") {
if let startWords = try? String(contentsOf: startWordsURL) {
let allWords = startWords.components(separatedBy: "\n")
rootWord = allWords.randomElement() ?? "silkworm"
return
}
}

fatalError("Could not load start.txt from bundle.")
}

func isOriginal(word: String) -> Bool {
!usedWords.contains(word)
}

func isPossible(word: String) -> Bool {
var tempWord = rootWord

for letter in word {
if let pos = tempWord.firstIndex(of: letter) {
tempWord.remove(at: pos)
} else {
return false
}
}

return true
}

func isReal(word: String) -> Bool {
let checker = UITextChecker()
let range = NSRange(location: 0, length: word.utf16.count)
let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en")
return misspelledRange.location == NSNotFound
}

func isOK(newWord: String) -> Bool {
let tempWord = rootWord
if newWord == tempWord || newWord.count < 3 {
return false
}
return true
}

func wordError(title: String, message: String) {
errorTitle = title
errorMessage = message
showingError = true
}
}

#Preview {
ContentView()
}

在这个实例里,创建警告弹框的大致思路是:

  1. 如果我想添加警告,我需要先定义一个showingError,默认值为false;
  2. 调用alert (isPresented: $showingError)
  3. 用一个方法addNewWord来总结不同的要弹出警告的方法;
  4. 使用 guard else 进行检查,如果为 false,则执行代码块中的方法:添加警告的标题、消息,并设置 shownError = true

如有错误,请及时指出~评论发邮件均可,欧内盖!

Other Articles
Article table of contents TOP
  1. 1. iOS的警告弹框
  2. 2. 实现一个简单的警告弹框
  3. 3. 显示两个按钮
  4. 4. 显示不同文案的警告弹框
  5. 5. 使用警告弹框的实例
Please enter keywords to search