【发布时间】:2023-09-21 20:45:01
【问题描述】:
我希望在 SwiftUI 中实现一个通用的验证/否决循环 - 使用“单一事实来源”框架应该非常简单的事情
总之我想:
- 拥有一个通用控件(比如
TextField) - 对该控件的更新应用验证/否决(例如,用户键入文本)
- 将预期的更改传播到验证器,在某处更新
Binding源对象(理想情况下,View内的@State成员) - 将该值反馈回控件以进行显示
对于所有“单一事实来源”的说法,Apple 似乎是在撒谎——在这个链中注入一个验证阶段似乎很困难,尤其是在不破坏视图封装的情况下
请注意,我并不想特别解决 this 问题 - 我正在寻找一种模式来实现(即:将 String 和 TextField 替换为 Bool例如Toggle)
以下代码显示了我在执行上述循环时的最佳尝试
class ValidatedValue<T>: ObservableObject {
let objectWillChange = ObservableObjectPublisher()
var validator: (T, T)->T
var value: T {
get {
_value
}
set {
_value = validator(_value, newValue)
objectWillChange.send()
}
}
/// Backing value for the observable
var _value: T
init(_ value: T, validator: @escaping (T, T)->T) {
self._value = value
self.validator = validator
}
}
struct MustHaveDTextField: View {
@ObservedObject var editingValue: ValidatedValue<String>
public var body: some View {
return TextField(
"Must have a d",
text: $editingValue.value
}
}
在View 范围之外定义的验证值
ValidatedValue(
"oddity has a d",
validator: { current, new in
if new.contains("d") {
return new
}
else {
return current
}
}
)
这种种 起作用,因为它会阻止您修改不包含“d”的字符串输入。然而;
- 光标状态仍然在文本控件上移动,超过了验证点
- 它暴露了应该完全是内部状态的内容,并需要从父母那里或通过
EnvironmentObject传递(如果你正在使用Lists 的东西......ow)
要么我遗漏了一些关键,要么我采用了错误的方法,要么 Apple 所说的不是 Apple 所做的。
像here 或here 那样在循环期间修改内部状态并不好——它们会修改视图循环内的状态,XCode 将其标记为undefined behaviour。 This one 也有类似的解决方案,但同样需要将验证逻辑 放在视图之外 - 恕我直言,它应该是独立的。
【问题讨论】:
标签: validation swiftui combine