ios - 如何在 SwiftUI View 上使用组合
<p><p>这个问题与这个问题有关:<a href="https://stackoverflow.com/questions/56735382/how-to-observe-a-textfield-value-with-swiftui-and-combine" rel="noreferrer noopener nofollow">How to observe a TextField value with SwiftUI and Combine?</a> </p>
<p>但我要问的是更笼统的问题。
这是我的代码:</p>
<pre><code>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)
}
}
</code></pre>
<p><strong>场景:</strong>当用户在文本字段中输入内容时,应该调用 <em>saveTextToFile</em> 函数。由于这是保存到文件,因此应该减慢/节流。 </p>
<p><strong>所以我的问题是:</strong></p>
<ol>
<li>在下面的代码中放置组合操作的正确位置是什么。</li>
<li>我要使用什么组合代码来完成:<strong>(A)</strong> 字符串不能包含空格。 <strong>(B)</strong> 字符串长度必须为 5 个字符。 <strong>(C)</strong> 字符串必须去抖动/减速</li>
</ol>
<p>我想在这里使用响应作为一般模式:<em>我们应该如何处理 SwiftUI 应用程序(而不是 UIKit 应用程序)中的组合内容。</em></p></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>你应该在你的 <code>ViewModel</code> 中做你想做的事。您的 View 模型是 <code>TestModel</code> 类(我建议您在 <code>TestViewModel</code> 中重命名它)。这是您应该在模型和 View 之间放置逻辑的地方。 <code>ViewModel</code> 应该准备好模型以便为可视化做好准备。这是放置组合逻辑的正确位置(当然,如果它与 View 相关)。</p>
<p>现在我们可以使用您的具体示例来实际制作示例。老实说,根据您<strong>真正</strong>想要实现的目标,有几个略有不同的解决方案。但现在我会尽量保持通用,然后你可以告诉我解决方案是否良好或需要一些改进:</p>
<pre><code>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 { 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)
}
}
</code></pre>
<p>您应该将自己的 <code>subscriber</code> 附加到 <code>TextField</code> <code>publisher</code> 并使用 <code>debounce</code> 发布者来延迟清理字符串和对保存方法的调用。根据文档:</p>
<blockquote>
<p>debounce(for:scheduler:options:)</p>
<p>Use this operator when you want to wait for a pause in the delivery of
events from the upstream publisher. For example, <strong>call debounce on the</strong>
<strong>publisher from a text field to only receive elements when the user
pauses or stops typing</strong>. When they start typing again, the debounce
holds event delivery until the next pause.</p>
</blockquote>
<p>当用户停止输入时,debounce 发布者会等待指定的时间(在我的示例中为 0.5 秒以上),然后使用新值调用其订阅者。</p>
<p>上述解决方案延迟<strong>两个</strong>字符串的保存<strong>和</strong> <code>TextField</code>更新。这意味着在更新发生之前,用户将在一段时间内看到原始字符串(带有空格且可能超过 5 个字符的字符串)。这就是为什么在这个答案的开头,我说根据需要有几种不同的解决方案。如果,确实,我们只想延迟保存字符串,但我们希望禁止用户输入空格字符或超过 5 个字符的字符串,我们可以使用两个订阅者(我将只发布更改的代码,即 <code>TestViewModel</code> 类):</p>
<pre><code>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 { in self.cleanText(text: $0.text) }
.sink { newText in
self.saveTextToFile(text: self.cleanText(text: newText))
}
updateCanc = $myproperty.sink { 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)")
}
}
</code></pre></p>
<p style="font-size: 20px;">关于ios - 如何在 SwiftUIView 上使用组合,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/57922766/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/57922766/
</a>
</p>
页:
[1]