SwiftUI UIViewRepresentable UITextView Binding

6

目前 SwiftUI 不支持本机多行文本输入(希望这个功能很快能被添加!),因此我一直在尝试使用 Combine 框架来实现自适应多行 UITextVIew 的输入功能,但是效果并不稳定。

以下是我编写的 Text 视图代码:

struct MultilineTextView: UIViewRepresentable {

    @Binding var text: String


    func makeUIView(context: Context) -> UITextView {
        let view = UITextView()
        view.isScrollEnabled = true
        view.isEditable = true
        view.isUserInteractionEnabled = true
        view.backgroundColor = UIColor.white
        view.textColor = UIColor.black
        view.font = UIFont.systemFont(ofSize: 17)
        view.delegate = context.coordinator
        return view
    }

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

    func frame(numLines: CGFloat) -> some View {
        let height = UIFont.systemFont(ofSize: 17).lineHeight * numLines
        return self.frame(height: height)
    }

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

    class Coordinator: NSObject, UITextViewDelegate {
        var parent: MultilineTextView

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

        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
        }
    }
}

我随后在SwiftUI视图中实现了它,如下所示:

MultilineTextView(text: title ? $currentItem.titleEnglish : $currentItem.pairArray[currentPair].english)//.frame(numLines: 4)

并将其绑定到状态变量:

@State var currentItem:Item

它有些可行。但是状态变量 currentItem:Item 包含一个字符串数组,我正在使用按钮循环它并基于输入到 MultilineTextView 中的内容更新字符串数组。这就是我遇到问题的地方,MultilineTextView 似乎只绑定到数组中的第一个字符串项,然后它就不会改变了。当我使用 swiftUI 的原生 TextField 视图时,这个功能运行正常,我可以循环访问字符串数组并通过在 TextField 中输入文本进行更新。
我认为在 MultilineTextView 结构体中必须缺失某些功能。欢迎任何指针。
更新:添加了模型结构。
struct Item: Identifiable, Codable {
    let id = UUID()
    var completed = false
    var pairArray:[TextPair]
}

struct TextPair: Identifiable, Codable {
    let id = UUID()
    var textOne:String
    var textTwo:String
}

编辑: 经过我进一步的调查,我发现了可能的问题。当UITextView的textViewDidChange被触发时,它确实会发送更新的文本,这些文本可以在控制台中看到。奇怪的是,updateUIView函数也会被触发,并使用更新之前绑定变量中的内容来更新UITextView的文本。结果是,当您键入内容时,UITextview就拒绝更改。奇怪的是,它对于数组中的第一个字符串有效,但是当项目更改后,它将不再起作用。


你可能会发现我在如何在SwiftUI中创建多行文本字段?中提供的方法很有帮助。 - Asperi
@Asperi,感谢您提供的潜在解决方案,但不幸的是,该代码仍然表现出相同的行为,即它只会绑定到数组中第一个文本实例,然后当其绑定的状态变量更改为数组中的另一个文本项时,它将不允许任何输入 :/ 。这很烦人,因为使用原生TextField可以正常工作。 - テッド
有趣...你能展示一下Item是什么吗? - Asperi
@Asperi感谢您对帖子的更新。为了澄清,这些项目存储在名为ItemStore的环境对象中,其中包含所有Item对象。 - テッド
这个问题几乎与此问题相同:https://dev59.com/ubnoa4cB1Zd3GeqPR4K1 请查看我在那里的解决方案。 - Xaxxus
显示剩余2条评论
2个回答

2
似乎SwiftUI会为每个绑定创建两个UIViewRepresentable的变体,但在状态(即title)切换时不会切换它们... 可能是由于缺陷,值得向Apple提交。

我找到了可以解决问题的方法(在Xcode 11.2 / iOS 13.2中进行了测试),如下所示,改为显式使用不同的视图。

if title {
    MultilineTextView(text: $currentItem.titleEnglish)
} else {
    MultilineTextView(text: $currentItem.pairArray[currentPair].textOne)
}

感谢您的解决方案。它适用于标题,但不适用于pairArray。我太愚蠢了,没有说“currentPair”也是一个状态变量,这就是我的问题所在。请参见下面的答案。 - テッド

0
最终我找到了问题所在,原因是因为我传递的字符串位于两个状态变量中。你可以在下面的代码中看到,currentItem是一个状态变量,而currentPair是另一个状态变量,它提供了一个索引号来定位字符串。后者没有被更新,因为它也没有通过绑定传递到多行文本视图中。
MultilineTextView(text: title ? $currentItem.titleEnglish : $currentItem.pairArray[currentPair].english)

一开始我认为只传入一个参数就可以了,父视图会处理另一个参数,但实际上并非如此。我通过创建两个绑定变量来解决问题,这样我就可以以动态方式找到所需的字符串。现在听起来很愚蠢,但当时我没看出来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接