Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating the UI for a tree structure in SwiftUI

I have an ObservableObject class, Model0 which has a Published object nested. The nested object represents a recursive tree structure. I want to be able to update the tree structure and have the UI update appropriately. However, in the below code, when I update a child of the nested object, the UI is not updated, I assume because the reference to nested is not changed. How can I get the UI to update whenever I change any of the nested objects in the tree structure?

class Nested: Identifiable {
    var id: UUID = UUID()
    var name: String
    var children:[Nested]
    
    init(name: String, children:[Nested]) {
        self.children = children
        self.name = name
    }
}

class Model0 : ObservableObject {
    @Published var nested: Nested
    
    init(nested: Nested) {
        self.nested = nested
    }
    
    func update() {
        nested.children[0] = Nested(name:"New", children: [])
    }
}

struct NestedView: View {
    var nested: Nested
    
    var body: some View {
        VStack {
            Text(nested.name)
            
            ForEach(nested.children) { c in
                NestedView(nested: c)
            }
        }
    }
}


struct Test: View {
    @ObservedObject var model: Model0 = Model0(nested: Nested(name: "Parent" ,children: [Nested(name: "Child", children: [])]))
    
    var body: some View {
        VStack {
            Button(action: {
                self.model.update()
            }) {
             Text("Update")
            }
            NestedView(nested: model.nested)
        }
    }
}
like image 370
BenJacob Avatar asked Oct 26 '22 18:10

BenJacob


1 Answers

If you decided to make reference-type model, then you need to make all of them observable, so each level could update corresponding view. And, of course, have separated view for each level model.

Taking above into account, here is a solution. Tested with Xcode 12 / iOS 14.

Modified part only:

class Nested: ObservableObject, Identifiable {
    var id: UUID = UUID()
    @Published var name: String
    @Published var children:[Nested]

    init(name: String, children:[Nested]) {
        self.children = children
        self.name = name
    }
}

struct NestedView: View {
    @ObservedObject var nested: Nested

    var body: some View {
        VStack {
            Text(nested.name)

            ForEach(nested.children) { c in
                NestedView(nested: c)
            }
        }
    }
}
like image 140
Asperi Avatar answered Nov 15 '22 06:11

Asperi