将状态/绑定传递给UIViewRepresentable

时间:2020-04-26 18:33:29

标签: ios swift swift5

将状态变量传递到自定义文本字段的正确方法是什么?我希望避免使用其他方法/可观察到的方法。这不行吗?

我在示例项目中重新创建了以下问题。

import SwiftUI

struct ParentView: View {
    @State var text: String = "initial"
    var body: some View {
        VStack {
            ChildView(text: $text)
            Text(self.text)
        }
    }
}
struct ChildView: View {
    @Binding var text: String
    var body: some View {
        MyTextField(text: $text).frame(width: 300, height: 40, alignment: .center)
    }
}

struct MyTextField: UIViewRepresentable {
    @Binding var text: String
    func makeUIView(context: Context) -> UITextField {
        let view = UITextField()
        view.borderStyle = UITextField.BorderStyle.roundedRect
        return view
    }
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
    }
}

2 个答案:

答案 0 :(得分:0)

即使@Binding var text: String之后,即使TextField的文本发生变化,绑定文本(uiView.text = text)也不会改变,因此您需要通过使用闭包来获取上下文来手动更新绑定文本包含绑定文本,TextField对象和值均衡的代码语句,并使用Delegate自动调用闭包。但是,TextField的Delegate(类UITextFieldDelegate)没有有效的方法来正确反映文本的更改(func textField总是比更改晚调用),因此如果使用Delegate(类,则使用TextView而不是TextField可能更好。 TextViewDelegate),因为它的Delegate具有textViewDidChange方法,该方法在更改TextView的文本时一定会被调用。 TextField或TextView的addTarget方法将不起作用,因为Obj-C函数(闭包)无法在swift结构中使用。 我在下面尝试了代码: (测试环境:Xcode 11.4.1,Swift 5.2.2)

//Add new:
var bindingUpdate:()->Void={}  //the definition the closure

//Add new:
class TextViewDelegate:UIViewController,UITextViewDelegate{
    func textViewDidChange(_ textView: UITextView) {
        bindingUpdate()  //call the closure while text changed
    }
}

//Modify:
struct MyTextView: UIViewRepresentable {
    @Binding var text: String
    var delegate=TextViewDelegate()
    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.font=UIFont(name: "Helvetica", size: 18)
        view.layer.borderWidth = 2;
        view.layer.cornerRadius = 16;
        view.delegate=delegate
        bindingUpdate={  //the closure
            self.text=view.text
            //add other lines here if you want make other effects while text changed
        }
        return view
    }
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
    }
}

我不确定语法是否合适 但效果很好

更新:

如果您仍然想使用TextField,这是一种更好的方法:您可以使用Coordinator更新绑定文本。

struct MyTextField: UIViewRepresentable {
    @Binding var text: String
    func makeUIView(context: Context) -> UITextField {
        let view = UITextField()
        view.borderStyle = .roundedRect
        view.addTarget(
            context.coordinator,
            action: #selector(Coordinator.updateText(sender:)),
            for: .editingChanged
        )
        return view
    }
    func updateUIView(_ uiView: UITextField, context: Context) {
        uiView.text = text
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    class Coordinator: NSObject{
        var myView: MyTextField

        init(_ view: MyTextField){
            self.myView=view
        }
        @objc func updateText(sender: UITextField){
            myView.text=sender.text!
        }
    }
}

这是适当的语法,它也可以使用,尽管它不适用于TextView:它没有方法addTarget。

答案 1 :(得分:0)

@Binding中创建一个CustomTextField属性,例如:

struct CustomTextField: UIViewRepresentable {
    @Binding var text: String 
}

@Binding中初始化init()属性,例如:

init(text: Binding<String>) {
    self._text = text
}

text属性传递给UITextField,例如:

func makeUIView(context: Context) -> UITextField {
    // Customise the TextField as you wish
    textField.text = text        
    return textField
}

更新text中的UITextField,例如:

func updateUIView(_ uiView: UITextField, context: Context) {
    uiView.text = self.text
}

使用用户输入的文本更新@Binding属性,例如:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    if let value = textField.text as NSString? {
    let proposedValue = value.replacingCharacters(in: range, with: string)
        parent.text = proposedValue as String // Here is updating
    }
    return true
}

您的ContentView应该如下所示:

struct ContentView: View {
    @State var text: String = ""
    var body: some View {
        CustomTextField(text: $text)
    }
}

现在,如果您想要CustomTextField的完整代码,请参见this answer