# Swift
力荐一个学习swift/swiftUI的网站:Hacking with Swift (注:本文章中的部分idea来自于Tiny66 )
iOS的警告弹框 在学习过程中,准确说是在Day31左右时,写的一个小app:WordScramble 中,我们需要写几个需要警告弹框的地方。然后我就发现,swiftUI中实现警告弹框的逻辑还挺有特点 —— 怎么说呢,个人感觉并不简洁…但是!还是很有逻辑的,所以在这里分享一下我的学习心得。
iOS中的警告弹框,就是如下的形式:
实现一个简单的警告弹框 首先,我们要声明一个 @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 SwiftUIstruct 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" ), dismissButton: .cancel() ) } } }
需要注意的是,在这个版本中,我们使用了Alert结构体来创建警告弹框。在iOS 15以后的版本中,我们也可以用如下的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import SwiftUIstruct 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) { } } message: { Text ("Message" ) } } }
其中,注释 1 和 2 部分的代码相同,注释 3 的代码不同,区别在于在iOS 15以后,我们不再需要使用Alert结构体,直接把按钮,标题,信息传递给.alert
即可。 另外需要注意的是,在我们点击alert中的按钮后,showingAlert
会自动切换回false,不需要手动进行。方便了下次调用,同时也是为什么警告弹框会在点击按钮后消失的原因。
实现的效果如下:
显示两个按钮 在理解基本的思路和原理后,如果想要实现警告弹框中有两个按钮,甚至多个按钮,就十分简单了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import SwiftUIstruct 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" )), secondaryButton: .cancel() ) } } }
与上面的区别就是,我们在这里使用了primaryButton
和secondaryButton
,用来显示两个按钮;而里面的.default()
是Alert.Button.default()
的缩写,.cancel
是Alert.Button.cancel()
的缩写。
此外,Alert.Button
还有另外一种样式:.destructive
,生成一个红色字体的按钮,常用于删除等操作时的提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import SwiftUIstruct 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 ("Confirm" ) { } } message: { Text ("Message" ) } } }
这样写的话,相比于一个按钮的版本,仅仅是增加了一行按钮而已,非常的简洁。
而如果要指定样式,则使用role
,和旧版本一样也是三种:
.default
,不需要指定role,显示蓝色文本;
.cancel
,role的值为.cancel,显示加粗的蓝色文本;
.destructive
,role的值为.destructive,显示红色文本。
实现的效果如下:
值得一提的是,如果使用iOS 15以后的这个版本,我们可以往其中添加任意多个按钮,不受限制。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import SwiftUIstruct 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" ) } } }
实现的效果如下:
显示不同文案的警告弹框
如果页面上有不同的几个按钮,点了之后要显示不同文案的弹框,可以这样做:
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 SwiftUIstruct AlertMessage : Identifiable { var id: String {message} var title: String var message: String } struct ContentView : View { @State private var alertMessage: AlertMessage ? var body: some View { VStack { Button ("Show Alert1" ) { alertMessage = AlertMessage (title: "Alert 1" , message: "message 1" ) } Button ("Show Alert2" ) { alertMessage = AlertMessage (title: "Alert 2" , message: "message 2" ) } } .alert(item: $alertMessage ) {message in Alert ( title: Text (message.title), message: Text (message.message), primaryButton: Alert .Button .default(Text ("Confirm" )), secondaryButton: Alert .Button .cancel() ) } } }
其中:
定一个AlertMessage
结构体,用来表示消息,因为要使用在.alert(item:content)
方法中,所以必须遵循Identifiable协议;
定义一个optional的状态变量alertMessage
,用来存放消息,程序运行时,如果计算出此变量为nil
,alert不显示,否则显示;
添加多个按钮,点击的时候将alertMessage
设置成不同的值;
调用.alert(item:content)
方法,绑定alertMessage
;
显示不同的message。
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 SwiftUIstruct AlertMessage : Identifiable { var id: String {message} var title: String var message: String } struct ContentView : View { @State private var alertMessage: AlertMessage ? @State private var $showingAlert = false var body: some View { VStack { Button ("Show Alert1" ) { alertMessage = AlertMessage (title: "Alert 1" , message: "message 1" ) $showingAlert = true } Button ("Show Alert2" ) { alertMessage = AlertMessage (title: "Alert 2" , message: "message 2" ) $showingAlert = true } } .alert(alertMessage? .title ?? "" , isPresented: $$showingAlert , presenting: alertMessage ) { 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 SwiftUIstruct 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 () }
在这个实例里,创建警告弹框的大致思路是:
如果我想添加警告,我需要先定义一个showingError
,默认值为false;
调用alert (isPresented: $showingError)
;
用一个方法addNewWord
来总结不同的要弹出警告的方法;
使用 guard else
进行检查,如果为 false,则执行代码块中的方法:添加警告的标题、消息,并设置 shownError = true
。
如有错误,请及时指出~评论发邮件均可,欧内盖!