如何添加 onCommit、isEditing 以及自定义绑定到自定义文本视图的能力?

How to add onCommit, isEditing, and ability to have a custom binding to a custom textview?

提供以下 UIViewRepresentable isEditing 和 onCommit 属性的最佳方法是什么?我希望它在 SwiftUI 中调用 TextFields 时具有完全相同的功能(您可以在其中添加代码以在按下 return 键 [onCommit] 或单击文本字段时执行操作 [isEditing])

struct TextView: UIViewRepresentable {
@Binding var text: String

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

func makeUIView(context: Context) -> UITextView {
    
    let myTextView = UITextView()
    myTextView.delegate = context.coordinator
    
    myTextView.font = UIFont(name: "HelveticaNeue", size: 16)
    myTextView.isScrollEnabled = true
    myTextView.isEditable = true
    myTextView.isUserInteractionEnabled = true
    myTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.00)
    myTextView.textColor = UIColor.black
    
    return myTextView
}

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

class Coordinator : NSObject, UITextViewDelegate {
    
    var parent: TextView
    
    init(_ uiTextView: TextView) {
        self.parent = uiTextView
        
    }
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if (text == "\n") {
            textView.resignFirstResponder()
        }
        return true
    }
    
    func textViewDidChange(_ textView: UITextView) {
        print("text now: \(String(describing: textView.text!))")
        self.parent.text = textView.text
    }
    
    
    
    
}
}

旁注:我不确定为什么,但我传入的绑定变量并没有改变实际值。我知道是因为我使用 get/set 属性设置了自定义绑定,并且该集合未打印该值。 (这个值应该是传入textview的文本)

let binding = Binding<String>(get: {
        self.bindingVariableName
    }, set: {
        print("set the value")
        self.bindingVariableName = [=11=]
    }
    )

onCommit

您可以将函数作为变量传递,使它们在正确的位置执行,例如:

struct TextView: UIViewRepresentable {
    @Binding var text: String

    var onCommit: ()->()
    ...
}

... {
        ...
        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            if (text == "\n") {
                textView.resignFirstResponder()
                parent.onCommit() // <- execute here
            }
            return true
        }
}

并像这样使用它:

TextView(text: $text, onCommit: {
    print("Committed")
})

可以应用类似的检测变化的方法


编辑

如果您使用的是 SwiftUI 2.0,您可以使用类似 onCommit 我上面解释的 的方法,您可以使用 .onChange 修饰符观察变化(如果实际值从 SwiftUI 一侧发生变化),例如:

struct ContentView: View {
    @State var text: String = ""

    var body: some View {
        TextView(text: $text)
            .onChange(of: text) { value in
                print("text now: " + text)
            }
    }
}

更新实际 @Binding 值:

您需要通过在 coordinator:

中实现此功能来更新 UIKit 中的 text
func textViewDidChange(_ textView: UITextView) {
    parent.text = textView.text
}

然后实际绑定变量在文本字段的文本更改事件上得到更新。


奖励

使用此方法,您还可以将配置步骤转发到SwiftUI端。所以重构后的完整工作代码如下:

struct ContentView: View {
    @State var text: String = ""

    var body: some View {
        TextView(text: $text) { // Configuring the text field
            [=14=].font = UIFont(name: "HelveticaNeue", size: 16)
            [=14=].isScrollEnabled = true
            [=14=].isEditable = true
            [=14=].isUserInteractionEnabled = true
            [=14=].backgroundColor = UIColor(white: 0.0, alpha: 0.00)
            [=14=].textColor = UIColor.black
        }

        onCommit: { // Detecting the commit
            print("Committed with text: " + text)
        }

        .onChange(of: text) { value in // The native way to detect changes of a State
            print("text now: " + text)
        }
    }
}

struct TextView: UIViewRepresentable {
    @Binding var text: String

    var configurate: (UITextView) -> ()
    var onCommit: ()->()

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

    func makeUIView(context: Context) -> UITextView {
        let myTextView = UITextView()
        configurate(myTextView) // Forwarding the configuring step
        myTextView.delegate = context.coordinator
        return myTextView
    }

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

    class Coordinator: NSObject, UITextViewDelegate {
        var parent: TextView

        init(_ uiTextView: TextView) {
            self.parent = uiTextView
        }

        func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
            if (text == "\n") {
                textView.resignFirstResponder()
                parent.onCommit() // Execute the passed `onCommit` method here
            }
            return true
        }

        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text // Update the actual variable
        }
    }
}