当所有文本字段在 SwiftUI 中都有值时,如何动态更新屏幕上的标签?

How do I dynamically update labels on a screen when all text fields have values in SwiftUI?

我正在开发一款允许用户使用 2 个单独的文本字段输入投注赔率的应用程序。例如,假设用户想要的赔率是 3/1。用户需要在一个字段中输入 3,在另一个字段中输入 1。

我有 2 个状态变量:

这就是我将它们与 CustomTextField 一起使用的方式:

CustomTextfield(text: $leftOddsVal, placeHolder: "Enter text", keyboardType: .numberPad)
CustomTextfield(text: $rightOddsVal, placeHolder: "Enter text", keyboardType: .numberPad)
CustomTextfield(text: $state, placeHolder: "Enter text", keyboardType: .numberPad)

这是自定义文本字段的代码:

struct CustomTextfield: UIViewRepresentable {

    @Binding var text : String
    var placeHolder: String
    var keyboardType: UIKeyboardType

    final class Coordinator: NSObject {

        var textField: CustomTextfield

        init(_ textField: CustomTextfield) {
            self.textField = textField
        }

    }

    func makeCoordinator() -> DabbleTextfield.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextField {

        let textField = UITextField()
        textField.backgroundColor = .white
        textField.placeholder = self.placeHolder
        textField.keyboardType = self.keyboardType

        return textField
    }

    func updateUIView(_ uiView: UITextField, context: Context) {

    }

}

我要做什么:

我想获取每个字段的值,并编写一些逻辑来计算可以赢多少和输多少,然后更新屏幕上的一些文本标签。我希望 win/lose 标签在所有字段都有值后自动更新,而不是让用户在他们看到他们可能赢或输之前提交表单。

这些是标签的外观:

问题:

  1. textFieldDidEndEditing 委托方法需要知道触发什么方法。这意味着,我需要修改 CustomTextField 以触发我的 viewModel 中将触发的方法。然后我可以触发win/lose计算方法。然而,我开始觉得,对于如此简单的事情来说,这需要做很多工作。看起来很乱

  2. 我无法直接在视图中访问文本字段值。如果我使用 SwiftUI 的 TextField 组件,这会很简单。但是,我正在使用包装在 UIRepresentable 结构中的 UIKit 的 UITextField 组件,如您在上面的代码中所见。

我试过的:

我有一个视图模型,我试图将其传递到 CustomtextField 中。

我只是这样声明我的环境变量:

   struct CustomTextfield: UIViewRepresentable {

      @EnvironmentObject var viewModel: EnvironmentObject<AnyObject>
      @Binding var text : String
      var placeHolder: String
      var keyboardType: UIKeyboardType

我使用了 AnyObject,这样我就可以将传递给 CustomTextField 的任何模型作为正确的类型进行投射。我不想手动修改 CustomTextField 来满足我可能使用它的每个模型。

我现在这样使用 CustomTextField:

   CustomTextfield(viewModel: betViewModel as! EnvironmentObject<BetViewModel>, value: $oddsLeftValue, placeHolder: "Enter text", keyboardType: .numberPad)

然后我将 TextField 的副本存储在我的 ViewModel 的一个变量中:

func makeUIView(context: Context) -> UITextField {
    let textField = UITextField()
    if let fieldValue = textField.text {
        self.viewModel.textField = fieldValue
    }

但我收到以下错误:

Type 'AnyObject' does not conform to protocol 'ObservableObject'

我也试过将绑定的文本值设置为文本字段的值,但这也没有用。

我希望有人能告诉我我做错了什么。我觉得我在兜圈子,而造成这种情况的原因是试图重用 UIRepresentable UIKit 组件。它似乎限制了我,这导致变通办法似乎对我想要实现的目标来说是一种矫枉过正。

我想要做的就是允许用户输入他们的赔率和赌注,并动态更新 win/lose 标签,而无需点击任何按钮。同时能够重用 CustomTextField。

期待您的建议。 提前致谢。

以下内容使您的自定义文本字段可操作...用于更新外部绑定,希望这是这么长的描述的目的。

测试 Xcode 11.4 / iOS 13.4

末尾的小 TestCustomTextfield 用于检查 CustomTextfield 中更改的更新标签。

struct CustomTextfield: UIViewRepresentable {

    @Binding var text : String
    var placeHolder: String
    var keyboardType: UIKeyboardType

    final class Coordinator: NSObject, UITextFieldDelegate {

        var parent: CustomTextfield

        init(_ parent: CustomTextfield) {
            self.parent = parent
        }

        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            parent.text = textField.text ?? ""
            return true
        }
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextField {

        let textField = UITextField()
        textField.backgroundColor = .white
        textField.placeholder = self.placeHolder
        textField.keyboardType = self.keyboardType
        textField.setContentHuggingPriority(.required, for: .vertical)
        textField.delegate = context.coordinator

        return textField
    }

    func updateUIView(_ uiField: UITextField, context: Context) {
        if let text = textField.text as NSString? {
           parent.text = text.replacingCharacters(in: range, with: string)
        } else {
           parent.text = ""
        }
     }

}

struct TestCustomTextfield: View {
    @State private var value = ""
    var body: some View {
        VStack {
            Text("Label: \(value)")
            CustomTextfield(text: $value, placeHolder: "Your value", keyboardType: .numberPad)
        }
    }
}