【问题标题】:Change on ObservableObject in @Published array does not update the view@Published 数组中 ObservableObject 的更改不会更新视图
【发布时间】:2020-07-31 11:16:48
【问题描述】:

我已经为 SwiftUI 的一个问题苦苦挣扎了好几个小时。

这是我的问题的简化示例:

class Parent: ObservableObject {
    @Published var children = [Child()]
}

class Child: ObservableObject {
    @Published var name: String?

    func loadName() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            // Async task here...
            self.objectWillChange.send()
            self.name = "Loaded name"
        }
    }

}

struct ContentView: View {
    @ObservedObject var parent = Parent()

    var body: some View {
        Text(parent.children.first?.name ?? "null")
            .onTapGesture {
                self.parent.objectWillChange.send()
                self.parent.children.first?.loadName() // does not update
            }
    }
}

我有一个 ObservableObject(父级)存储一个 @Published 的 ObservableObjects(子级)数组。

问题在于,当通过数组中一个对象上的异步任务更改名称属性时,视图不会更新。

你有什么想法吗?

非常感谢 尼古拉斯

【问题讨论】:

标签: swift swiftui combine observedobject


【解决方案1】:

确保您的孩子模型是struct!类没有正确更新 UI。

【讨论】:

  • 有一点需要注意,在 SwiftUI 2.0 中,@Published 似乎只适用于类的属性。
【解决方案2】:

我会说这是设计问题。请在下面找到仅使用纯 SwiftUI 功能且不需要任何解决方法的优选方法。关键思想是“视图-视图模型”的分解和显式依赖注入。

使用 Xcode 11.4 / iOS 13.4 测试

class Parent: ObservableObject {
    @Published var children = [Child()]
}

class Child: ObservableObject {
    @Published var name: String?

    func loadName() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            // Async task here...
            self.name = "Loaded name"
        }
    }
}

struct FirstChildView: View {
    @ObservedObject var child: Child
    var body: some View {
        Text(child.name ?? "null")
            .onTapGesture {
                self.child.loadName()
            }
    }
}

struct ParentContentView: View {
    @ObservedObject var parent = Parent()

    var body: some View {
        // just for demo, in real might be conditional or other UI design
        // when no child is yet available
        FirstChildView(child: parent.children.first ?? Child())
    }
}

【讨论】:

    【解决方案3】:

    这种替代方法对我有用:

    class Parent: ObservableObject {
    @Published var children = [Child()]
    }
    
    class Child: ObservableObject {
    @Published var name: String?
    
    func loadName(handler: @escaping () -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            // Async task here...
            self.name = UUID().uuidString  // just for testing
            handler()
        }
    }
    }
    
    struct ContentView8: View {
    @ObservedObject var parent = Parent()
    var body: some View {
        Text(parent.children.first?.name ?? "null").padding(10).border(Color.black)
            .onTapGesture {
                self.parent.children.first?.loadName(){
                    self.parent.objectWillChange.send()
                }
        }
    }
    } 
    

    【讨论】:

      猜你喜欢
      • 2021-10-14
      • 2020-01-07
      • 1970-01-01
      • 1970-01-01
      • 2020-09-13
      • 2021-04-01
      • 1970-01-01
      • 2021-03-19
      相关资源
      最近更新 更多