文本字段未更新 [英] Textfield not updating
问题描述
以下代码加载正常,文本和双字段都显示 300.但是,当我单击提交时,文本仅更改为 0.您将在打印输出中看到使用新值 0 调用了 init,但是它不更新双字段?有什么建议吗?
the following code loads fine, the text and the double field both show 300. however when I click submit the text only changes to 0. you will see in the print output that the init was called with the new value of 0 but it does not update the double field? any suggestions?
import SwiftUI
struct ContentView: View {
@State var amount:Double = 300
var body: some View {
VStack {
Text("\(self.amount)")
DoubleField(value: $amount)
Button(action: {self.amount=0}) {Text("Submit")}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct DoubleField:View {
@Binding var doubleValue:Double
@State var stringValue:String
init(value: Binding<Double>) {
print("DoubleField.init(value: \(value.wrappedValue))")
self._doubleValue = value
self._stringValue = State(initialValue: "\(value.wrappedValue)")
}
var body: some View {
TextField("0.00", text: $stringValue)
}
}
推荐答案
SwiftUI 不知道 DoubleField View 的内部状态发生了变化.在您设置 _stringValue
时,DoubleField
实例仍然不存在,这就是您无法在 init(..) State 属性中写入 stringValue = ... 的原因包装器只能在初始化后通知 SwiftUI 其包装值已更改,而不是之前.
The SwiftUI doesn't have any idea that internal state of DoubleField View changed. At the time you set _stringValue
, DoubleField
instance still doesn't exists, that is why you not able to write stringValue = ... in init(..) State property wrapper could inform SwiftUI only after initialization that its wrapped value was changed, not before.
尝试下一个
import SwiftUI
struct ContentView: View {
@State var amount:Double = 300
var body: some View {
VStack {
Text("\(self.amount)")
DoubleField(value: $amount).id(amount)
Button(action: {
self.amount += 10
}) { Text("Submit") }
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct DoubleField:View {
@Binding var doubleValue:Double
@State var stringValue:String
init(value: Binding<Double>) {
print("DoubleField.init(value: \(value.wrappedValue))")
self._doubleValue = value
self._stringValue = State(initialValue: "\(value.wrappedValue)")
print(stringValue)
}
var body: some View {
VStack {
Text(stringValue)
TextField("20.00", text: $stringValue)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
所有字段都根据需要更新,因为 SwiftUI 看到了由于 .id 修饰符而发生的变化
all fields are updating as required, because SwiftUI see the change due the .id modifier
在你的情况下只需使用
DoubleField(value: $amount).id(amount)
之所以有效,是因为 Double 符合 Hashable 并且只有在数量值发生变化时才会重新计算其主体.
It works because Double conform to Hashable and it will recalculeta its body only when amount value changed.
更新
您可以创建自定义绑定,就像我在下一个示例中所做的那样.它与用@State 包裹的后端 Double 值绑定(通知 SwiftUI 其值的任何更改)
You can create custom binding, as I did in next example. It is Binding with backend Double value wrapped with @State (to inform SwiftUI about any change of its value)
let binding = Binding<String>(get: {
if self.empty { return "" } else {
return self.format(value: self.value.value)
}
}) { (str) in
self.empty = str.isEmpty
// before string to double
// replace decimal separator to "." !!!
let dot = Locale.current.decimalSeparator ?? "."
let s = str.replacingOccurrences(of: dot, with: ".")
self.value.value = Double(s) ?? 0.0
}
哪里
func format(value: Double) -> String {
// for presentation
// replace "." with decimal separator
let dot = Locale.current.decimalSeparator ?? "."
var s = String(format: "%f", value)
.replacingOccurrences(of: ".", with: dot)
.reversed()
.drop(while: {$0 == "0"})
.reversed()
.map(String.init)
.joined()
if let last = s.last, String(last) == dot {
s.removeLast()
}
return s
}
它与 TextField 的配合比 TextField 中的 Formatter 内置支持要好得多,并且还支持 Locale 相关的十进制分隔符
It works with TextField much better than Formatter build-in support in TextField and also support Locale dependent decimal separator
和示例用法
使用
TextField("0", text: binding)
TextField("0", text: binding)
这篇关于文本字段未更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!