引子
自從Swift 5.1加入了Combine之後,腰不酸了,腿不疼了,各種姿勢都能有了…
我真不是在開車…
不過對於SwiftUI中的TextField來說如何實時監聽用戶輸入的文本呢?
貌似TextField自身並沒有帶用戶輸入改變的回調,這可咋整呢?
第一種方式:String.Publisher
沒關係,聰明的你們應該早已猜到,用Combine庫,秒天秒地秒空氣…
truct NameInputView: View{
@State var name:String = ""
var body: some View {
var body: some View{
VStack{
Text("New keyword: ")
.padding(.bottom)
TextField("Keyword Content", text: $name){
// 當用戶點擊返回按鈕時,關閉鍵盤
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
}
}
注意我們將name的綁定而不是單純name作爲TextField構造器的輸入。
在TextField最後加上一個modifer:
.onReceive(name.publisher){n in
print("get \(n)")
}
上面我可以看到Combine對現有庫中的幾乎所有類型增加了擴展,自然包括String,我們利用這一特性,免費爲字符串name生成了一個發佈者,只要監聽它,就可以監聽用戶輸入的改變了 😉
但是先Wait一下,如果你實際運行代碼,你會發現監聽的結果不是你想要的字符串,而是一個個分散的字符.
比如你輸入abc,打印的將會是:
get a
get b
get c
它不是完整的返回字符串"abc",而是分別返回其中的每個字符…
這可不是我們想要的結果!
沒關係,別忘了Combine裏強大的操作符(Operators),你一定立即就能想到確認過眼神對的那一個:reduce!
reduce操作符的作用是將上層發佈者的Output進行聚合,然後作爲自己的輸出。
將.onReceive的調用按如下修改:
.onReceive(name.publisher.reduce("", {t,c in
t + String(c)
})){
// 原代碼內容不變
}
再運行一下,啊哈!正是我們想要的!!!
上面就是第一種方式了,下面我們來看看還有沒有其他監聽輸入的方式了?
這是必須的!
第二種方式: CurrentValueSubject
Combine自帶了超多的Publisher和Operator,其中有一個CurrentValueSubject發佈者,可以爲你解憂:
let subject = CurrentValueSubject<String, Never>(name)
return
/*
原來的代碼
*/
.onReceive(subject){}
CurrentValueSubject是一個包含當前輸入值的發佈者,我們可以利用它來訪問當前保存的輸入值,非常方便!
第三種方式: String包裝器
最後一種方式是,雖然不能直接監聽String.Publisher,但是我們可以work around啊!
添加以下輔助類:
class StringWrapper: ObservableObject{
@Published var name = ""
}
將View的name屬性類型修改爲StringWrapper:
@State private var nameWrapper = StringWrapper()
最後將監聽代碼修改如下:
.onReceive(nameWrapper.$name){}
OK!運行一下,你會發現結果都是相同的。
結語
本篇我們使用了三種不同方式完成了對SwiftUI中輸入框的監聽
留存以便本貓回憶,也希望大家能學到一丟丟東西。
感謝觀賞,再會!