Изменение в ObservableObject в массиве @Published не обновляет представление - PullRequest
0 голосов
/ 17 апреля 2020

Я часами боролся за проблему со 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 (Parent), хранящий @ Опубликованный массив ObservableObjects (Child).

Проблема заключается в том, что при изменении свойства имени с помощью асинхронной задачи c для одного объекта в массиве представление не обновляется.

У вас есть идеи?

Большое спасибо, Николас

Ответы [ 3 ]

0 голосов
/ 18 апреля 2020

Я бы сказал, что это проблема дизайна. Ниже приведен предпочтительный подход, использующий только чистую функцию SwiftUI и не требующий обходных путей. Ключевой идеей является декомпозиция и явное внедрение зависимостей для «модели представления вида».

Протестировано с Xcode 11.4 / iOS 13.4

demo

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())
    }
}
0 голосов
/ 18 апреля 2020

Убедитесь, что модель вашего ребенка struct! Классы не обновляют интерфейс должным образом.

0 голосов
/ 18 апреля 2020

у меня работает этот альтернативный подход:

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()
            }
    }
}
} 
...