这个问题与这个问题有关: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 函数。由于这是保存到文件,因此应该减慢/节流。
所以我的问题是:
- 在下面的代码中放置组合操作的正确位置是什么。
- 我要使用什么组合代码来完成:(A) 字符串不能包含空格。 (B) 字符串长度必须为 5 个字符。 (C) 字符串必须去抖动/减速
我想在这里使用响应作为一般模式:我们应该如何处理 SwiftUI 应用程序(而不是 UIKit 应用程序)中的组合内容。
Best Answer-推荐答案 strong>
你应该在你的 ViewModel 中做你想做的事。您的 View 模型是 TestModel 类(我建议您在 TestViewModel 中重命名它)。这是您应该在模型和 View 之间放置逻辑的地方。 ViewModel 应该准备好模型以便为可视化做好准备。这是放置组合逻辑的正确位置(当然,如果它与 View 相关)。
现在我们可以使用您的具体示例来实际制作示例。老实说,根据您真正想要实现的目标,有几个略有不同的解决方案。但现在我会尽量保持通用,然后你可以告诉我解决方案是否良好或需要一些改进:
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 发布者来延迟清理字符串和对保存方法的调用。根据文档:
debounce(for:schedulerptions
Use this operator when you want to wait for a pause in the delivery of
events from the upstream publisher. For example, call debounce on the
publisher from a text field to only receive elements when the user
pauses or stops typing. When they start typing again, the debounce
holds event delivery until the next pause.
当用户停止输入时,debounce 发布者会等待指定的时间(在我的示例中为 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)")
}
}
关于ios - 如何在 SwiftUI View 上使用组合,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/57922766/
|