【问题标题】:How do I dynamically update labels on a screen when all text fields have values in SwiftUI?当所有文本字段在 SwiftUI 中都有值时,如何动态更新屏幕上的标签?
【发布时间】:2020-07-14 19:34:13
【问题描述】:

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

我有 2 个状态变量:

  • leftOddsVal
  • rightOddsVal

这就是我将它们与 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) {

    }

}

我想做的事:

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

这些是标签的外观:

问题:

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

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

我的尝试:

我有一个视图模型,我试图将它传递到 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'

我也尝试将绑定的文本值设置为 textField 的值,但这也不起作用。

我希望有人可以让我了解我做错了什么。我觉得我在绕圈子,原因是试图重用 UIRepresentable UIKit 组件。这似乎限制了我,这导致解决方法似乎对我想要实现的目标有点过头了。

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

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

【问题讨论】:

    标签: ios swift cocoa-touch uitextfield swiftui


    【解决方案1】:

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

    使用 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)
            }
        }
    }
    

    【讨论】:

    • 嗨,我添加了一行来在委托方法被触发时打印字段的值,但由于某种原因,第一次触发时,值是空白,然后是第二次当它被触发时,它会显示第一次触发时应该显示的值,依此类推。我在“return true”行之前添加了该行。
    • @LondonGuy,解决了这个问题。
    • 这让我得到了答案。 updateUIView 中的代码放错地方了。
    猜你喜欢
    • 1970-01-01
    • 2021-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 2017-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多