这个问题与此有关:How to observe a TextField value with SwiftUI and Combine?
但是我要问的有点笼统。 这是我的代码:
struct MyPropertyStruct {
var text: String
}
class TestModel : ObservableObject {
@Published var myproperty = MyPropertyStruct(text: "initialText")
func saveTextToFile(text: String) {
print("this function saves text to file")
}
}
struct ContentView: View {
@ObservedObject var testModel = TestModel()
var body: some View {
TextField("", text: $testModel.myproperty.text)
}
}
方案::当用户在文本字段中键入内容时,应调用 saveTextToFile 函数。由于这是保存到文件,因此应放慢/限制它。
所以我的问题是:
我想在此处将响应用作以下常规模式:我们应如何处理SwiftUI应用(而不是UIKit应用)中的合并内容。
答案 0 :(得分:7)
您应该在ViewModel
中进行所需的操作。您的视图模型是TestModel
类(我建议您在TestViewModel
中对其重命名)。在这里应该将逻辑放在模型和视图之间。 ViewModel
应该准备好模型以便进行可视化。这是放置合并逻辑的正确位置(当然,如果它与视图有关)。
现在,我们可以使用您的特定示例来实际举例。老实说,根据您确实想要实现的目标,有几种稍微不同的解决方案。但是现在,我将尝试尽可能通用一些,然后您可以告诉我解决方案是否完善或需要一些改进:
struct MyPropertyStruct {
var text: String
}
class TestViewModel : ObservableObject {
@Published var myproperty = MyPropertyStruct(text: "initialText")
private var canc: AnyCancellable!
init() {
canc = $myproperty.debounce(for: 0.5, scheduler: DispatchQueue.main).sink { [unowned self] newText in
let strToSave = self.cleanText(text: newText.text)
if strToSave != newText.text {
//a cleaning has actually happened, so we must change our text to reflect the cleaning
self.myproperty.text = strToSave
}
self.saveTextToFile(text: strToSave)
}
}
deinit {
canc.cancel()
}
private func cleanText(text: String) -> String {
//remove all the spaces
let resultStr = String(text.unicodeScalars.filter {
$0 != " "
})
//take up to 5 characters
return String(resultStr.prefix(5))
}
private func saveTextToFile(text: String) {
print("text saved")
}
}
struct ContentView: View {
@ObservedObject var testModel = TestViewModel()
var body: some View {
TextField("", text: $testModel.myproperty.text)
}
}
您应该将自己的subscriber
附加到TextField
publisher
上,并使用debounce
发布者来延迟字符串的清理和对save方法的调用。根据文档:
去抖动(for:scheduler:options:)
当您要等待传送的暂停时使用此运算符 来自上游发布者的事件。例如,呼叫在上进行反跳 来自文本字段的发布者,仅当用户 暂停或停止输入。当他们再次开始键入时,反跳 保持事件传递直到下一个暂停。
当用户停止键入时,防抖动发布者将等待指定的时间(在我的示例中为0.5秒以上),然后使用新值调用其订户。
上述解决方案会同时延迟和保存字符串和 TextField
。这意味着在更新发生之前,用户会看到原始字符串(带有空格且可能超过5个字符的字符串)一段时间。这就是为什么在这个答案的开头,我说根据需要有几种不同的解决方案。如果确实确实要延迟字符串的保存,但是我们希望禁止用户输入空格字符或长度超过5个字符的字符串,则可以使用两个订阅者(我将仅发布更改的代码,即TestViewModel
类):
class TestViewModel : ObservableObject {
@Published var myproperty = MyPropertyStruct(text: "initialText")
private var saveCanc: AnyCancellable!
private var updateCanc: AnyCancellable!
init() {
saveCanc = $myproperty.debounce(for: 0.5, scheduler: DispatchQueue.main)
.map { [unowned self] in self.cleanText(text: $0.text) }
.sink { [unowned self] newText in
self.saveTextToFile(text: self.cleanText(text: newText))
}
updateCanc = $myproperty.sink { [unowned self] newText in
let strToSave = self.cleanText(text: newText.text)
if strToSave != newText.text {
//a cleaning has actually happened, so we must change our text to reflect the cleaning
DispatchQueue.main.async {
self.myproperty.text = strToSave
}
}
}
}
deinit {
saveCanc.cancel()
updateCanc.cancel()
}
private func cleanText(text: String) -> String {
//remove all the spaces
let resultStr = String(text.unicodeScalars.filter {
$0 != " "
})
//take up to 5 characters
return String(resultStr.prefix(5))
}
private func saveTextToFile(text: String) {
print("text saved: \(text)")
}
}