Swift 和 SwiftUI 从 TextField 字符串绑定到模型的已发布可选整数 属性

Swift and SwiftUI binding from TextField string to model's published, optional Integer property

我是 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)
    }
}

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 class 中的自定义 UITextView 似乎是最常见的。

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

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

var string = ""

let myBinding = Binding(get: { string }) {
    string = [=10=]
}

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

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

所以,如果你想创建一个 Binding<String> 来传递给 gets/sets appModel.intValueTextField,你可以创建一个 Binding<String> 和像这样将它传递到 TextField 中:

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