文本字段未更新 [英] Textfield not updating

查看:31
本文介绍了文本字段未更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码加载正常,文本和双字段都显示 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆