对ObservedObject的更改不会更新UIViewRepresentable

时间:2019-10-12 12:15:27

标签: swiftui uiviewrepresentable

下面是最简单的示例。在预览中工作正常(UITextView文本更新为“ ouch”)。但是,在应用程序中运行它(例如,由sceneDelegate添加为rootView),UITextView不会更新。


class ModelObject : ObservableObject{
    @Published var text = "Model Text"
}

struct MyTextView : UIViewRepresentable {
    @ObservedObject var modelObject : ModelObject

    func makeUIView(context: Context) -> UITextView {
        let result = UITextView()
        result.isEditable = true
        return result
    }
    func updateUIView(_ view: UITextView, context: Context) {
        view.text = modelObject.text
    }
}

struct BugDemoView : View{
    @ObservedObject var modelObject : ModelObject
    var body : some View{
        VStack{
            MyTextView(modelObject: modelObject)
            Button(action: {
                self.modelObject.text = "ouch"
            }){
                Text("Button")
            }
        }
    }
}

#if DEBUG

var mo = ModelObject()

struct BugDemoView_Preview: PreviewProvider {
    static var previews: some View {
        BugDemoView(modelObject: mo)
    }

}
#endif

4 个答案:

答案 0 :(得分:0)

它看起来像是SwiftUI的某种错误,但是有两种解决方法:

  1. 将字符串作为@Binding传递到MyTextView
struct MyTextView : UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UITextView {
        let result = UITextView()
        result.isEditable = true
        return result
    }
    func updateUIView(_ view: UITextView, context: Context) {
        view.text = text
    }
}

struct BugDemoView : View{
    @ObservedObject var modelObject = ModelObject()
    var body : some View{
        VStack{
            MyTextView(text: $modelObject.text)
            Button(action: {
                self.modelObject.text = "ouch"
            }){
                Text("Button")
            }
        }
    }
}
  1. 将字符串传递到MyTextView
struct MyTextView : UIViewRepresentable {
    var text: String

    func makeUIView(context: Context) -> UITextView {
        let result = UITextView()
        result.isEditable = true
        return result
    }
    func updateUIView(_ view: UITextView, context: Context) {
        view.text = text
    }
}

struct BugDemoView: View{
    @ObservedObject var modelObject = ModelObject()
    var body: some View{
        VStack{
            MyTextView(text: modelObject.text)
            Button(action: {
                self.modelObject.text = "ouch"
            }){
                Text("Button")
            }
        }
    }
}

答案 1 :(得分:0)

我使用以下方法得出的结果参差不齐。我很确定UIViewRepresentable

中存在某种错误

它适用于某些视图,但是随后我再次推送了完全相同的视图,并且视图模型不会更新。很奇怪...

希望他们很快会发布一个SwiftUI TextView。

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: 15)
        myTextView.isScrollEnabled = true
        myTextView.isEditable = true
        myTextView.isUserInteractionEnabled = true
        myTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
        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 textViewDidChange(_ textView: UITextView) {
            print("text now: \(String(describing: textView.text!))")
            self.parent.text = textView.text
        }
    }
}

答案 2 :(得分:0)

在我的情况下,我需要将一个可观察对象传递给uiViewRepresentable,因此与Rivera提到的情况相同...当该可观察对象的@Published属性发生更改时,会在模拟器中调用updateUIView,而不是在真实对象上调用设备...我使用的是最新的Xcode 11.4,但是请注意,该设备是在运行iOS 13.3的设备上安装的(尚未安装13.4.1,因此我尚未检查该错误是否已消除)。解决我的问题的方法如下:将结构MyTextView更改为final类(final关键字很重要),然后添加一个初始化程序。甚至不必在触发已发布的var的更改之前就在观察的对象上调用.objectWillChange.send()。

答案 3 :(得分:0)

我通过打印语句注意到在第一个 URL 成功加载后没有再次调用 makeUIView。因此,在更改 URL 时,我的 WebView 仍保留在第一个 URL 上。

我修改了我的 updateUIView 方法 - 在第一次成功加载后更新 URL 时调用该方法 - 以检查活动 url 和新 url 之间是否存在差异。如果有差异,我会使用正确的网址更新页面。

这是我的示例 updateUIView 方法:

let request: URLRequest

func updateUIView(_ uiView: WKWebView, context: Context) {
    if uiView.canGoBack, webViewStateModel.goBack {
        uiView.goBack()
        webViewStateModel.goBack = false
    } else {
        if(uiView.url?.absoluteString == request.url?.absoluteString){
            print("The urls are equal")
        } else {
            print("The urls are NOT equal")
            uiView.load(request)
        }
    }
}