【问题标题】:Swift and SwiftUI binding from TextField string to model's published, optional Integer property从 TextField 字符串到模型已发布的可选 Integer 属性的 Swift 和 SwiftUI 绑定
【发布时间】:2020-05-21 23:05:19
【问题描述】:

我是 Swift 和 SwiftUI 的新手,并且有一个带有 @Published intValue 可选的模型和一个带有 TextField 的视图,其中它的 text/string 属性是双向绑定到模型的 intValue 的。需要确保作为字符串的有效文本条目被转换为模型的有效整数。在 SwiftUI 中处理此问题的推荐方法是什么?代码类似于下面...

//Model
class AppModel:ObservedObject {
 @Published
 var intValue: Int? = 5
}

//View
@ObservedObject
 var appModel = AppModel.shared

TextField("", text: $appModel.intValue)
                    .keyboardType(.numberPad)

更新示例代码...

///////////
import SwiftUI

class Model: ObservableObject {
    static var shared: Model = Model()

    @Published
    var number: Int = 0

    init(){}

}

struct ContentView: View {
    var body: some View {
        TextFieldFormatter()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct TextFieldFormatter: View {
    @ObservedObject
    var model: Model = Model.shared

    @State private var number: Int = 0


    let formatter: NumberFormatter = {
        let numFormatter = NumberFormatter()
        numFormatter.numberStyle = .none
        return numFormatter
    }()

    var body: some View {
        VStack {
            Text("State").onTapGesture {
                self.endEditing()
            }
            TextField("int", value: $number, formatter: formatter).keyboardType(.numberPad)
            Text("Echo: \(number)")
            Text("Observable")
            TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
            Text("Echo: \(model.number)")
        }
    }
}

struct TextFieldFormatter_Previews: PreviewProvider {
    static var previews: some View {
        TextFieldFormatter()
    }
}

extension View {
    func endEditing() {
        UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to:nil, from:nil, for:nil)
    }
}

【问题讨论】:

    标签: ios swift uitextfield swiftui textfield


    【解决方案1】:

    SwiftUI 的 TextField 有几个构造函数。其中一个采用 Formatter 参数。

    https://developer.apple.com/documentation/swiftui/textfield

    import SwiftUI
    
    struct TextFieldFormatter: View {
        @ObservedObject var model: Model = Model.shared
    
        let formatter: NumberFormatter = {
            let numFormatter = NumberFormatter()
            numFormatter.numberStyle = .none
            return numFormatter
        }()
    
        var body: some View {
            VStack {
                Text("State").onTapGesture {
                    self.endEditing()
                }
                TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
                Text("Echo: \(model.number)")
                Text("Observable")
                TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
                Text("Echo: \(model.number)")
            }
        }
    }
    
    struct TextFieldFormatter_Previews: PreviewProvider {
        static var previews: some View {
            TextFieldFormatter()
        }
    }
    

    此外,如果您尝试使用 onTapGesture 禁用屏幕,您可能会考虑。

    添加:

       @State var disabled: Bool = false
    

    和变化:

           Form {
                Text("Disable").onTapGesture {
                    //self.endEditing()
                    self.disabled = true
                }
                TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
                Text("Echo: \(model.number)")
                Text("Observable")
                TextField("int", value: $model.number, formatter: formatter).keyboardType(.numberPad)
                Text("Echo: \(model.number)")
            }.disabled($disabled.wrappedValue)
    

    可以通过几种不同的方式在用户输入时提供反馈,但UIViewRepresentable 类中的自定义UITextView 似乎是最常见的。

    【讨论】:

    • 我用您的代码更新了示例代码并添加到@Observable 对象中。当对文本字段进行更改时,使用状态的绑定变量和使用观察对象的模型仍然显示不更新。
    • 我做了一个小改动。按下 Return 键后,删除 State var 并引用 model.number 一切正常
    • 但是例如新的TextEditor只提供Binding<String>构造函数。这让事情变得很麻烦。
    【解决方案2】:

    您可以创建自定义Binding 以传递给TextField

    这是构造Binding<String> 的方法(TextField 需要什么):

    var string = ""
    
    let myBinding = Binding(get: { string }) {
        string = $0
    }
    

    Binding 构造函数的第一个参数是 getter。此闭包应返回所需类型的值。

    第二个参数是一个setter。它是一个接收新值的闭包,并对其进行处理(通常设置另一个值)。

    因此,如果您想创建一个Binding<String> 以传递给获取/设置appModel.intValueTextField,您可以创建一个Binding<String> 并将其传递给TextField,如下所示:

    TextField("", text: .init(
        get: { self.appModel.intValue.map(String.init) ?? "" },
        set: { self.appModel.intValue = Int($0) }
    ))
    .keyboardType(.numberPad)
    

    【讨论】:

    • 出现错误无法在地图函数上推断通用参数 T。进行了一些更改并提出了一些可以编译和更新的内容,但是使用这种方法编辑文本字段是有问题的。可以追加数字但不能退格删除数字。这是我使用的代码。 TextField("int", text: myBinding) let myBinding = Binding( get: { String(self.appModel.intValue)}, set: { self.appModel.intValue = (Int($0) ?? 5)})跨度>
    • 您不希望 appModel.intValue 成为可选项吗?
    猜你喜欢
    • 2020-08-11
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 2014-11-11
    • 2021-12-15
    相关资源
    最近更新 更多