如何在 SwiftUI 视图上使用组合
Posted
技术标签:
【中文标题】如何在 SwiftUI 视图上使用组合【英文标题】:How to use Combine on a SwiftUI View 【发布时间】:2019-09-13 11:25:35 【问题描述】:这个问题与这个问题有关: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 应用程序)中的组合内容。
【问题讨论】:
【参考方案1】:你应该在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
发布者来延迟字符串的清理和对保存方法的调用。根据文档:
去抖动(for:scheduler:options:)
当您想等待交付的暂停时使用此运算符 来自上游发布者的事件。例如,在上调用 debounce 发布者从一个文本字段只接收用户时的元素 暂停或停止输入。当他们再次开始打字时,去抖动 将事件传递保持到下一次暂停。
当用户停止输入时,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)")
【讨论】:
看起来代码在我运行时出现了一些问题。有时在 0.5 秒后,我在文本字段中的文本被删除。但这是一个很好的起点,让我试试你提供的东西,看看我是否可以改进它。如果我破解它,我会返回以将其标记为正确。 嗨@iosCalendarpatchthecode.com 您是在模拟器上还是在预览版上尝试代码?因为我无法在实际模拟器中重现该错误。 @iOSCalendarpatchthecode.com 同样,请务必复制粘贴当前版本的代码,我进行了多次编辑。 @iOSCalendarpatchthecode.com 啊,好吧,不用担心 xD 我开始考虑 MacOS 上的一些错误,iPhone 模拟器中不存在。以上是关于如何在 SwiftUI 视图上使用组合的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI之深入解析如何使用组合矩形GeometryReader创建条形(柱状)图