Home > Blockchain >  SwiftUI UI won't Update when ObservableObject Property Changes
SwiftUI UI won't Update when ObservableObject Property Changes

Time:09-06

In the following example, I have an ObservableObject which holds a list of Floor, a view that renders the floor list, and a button that update the list when clicked.

I expect that when button is clicked, the view will update from "ABC" to "XYC", however, it won't update at all.

struct Floor: Identifiable {
    let id: Int
    let content: String
}

class ViewModel: ObservableObject {
    @Published var floors = [
        Floor(id: 1, content: "A"),
        Floor(id: 2, content: "B"),
        Floor(id: 3, content: "C")
    ]
    
    func alter() {
        floors[0...1] = [
            Floor(id: 1, content: "X"),
            Floor(id: 2, content: "Y"),
        ]
    }
}

struct ListBlock: View {
    @State var floor: Floor
  
    // floor may be updated from within
    
    var body: some View {
        Text(floor.content)
    }
}

struct ListView: View {
    @ObservedObject var vm = ViewModel()

    var body: some View {
        List {
            Button("Alter") {
                vm.alter()
            }
            
            ForEach(vm.floors) { floor in
                ListBlock(floor: floor)
            }
        }
    }
}

I'm wondering whether there is a way for me to update the view when the underlying data changes?

Note: I use @State in ListBlock as I may update the floor from within the ListBlock view.

CodePudding user response:

To change the data from the Childview change that wrapper to a @Binding. Pass that on with the $ syntax. Also if you create your ViewModel you should wrap it in an @StateObject wrapper.

struct ListBlock: View {
    // add a binding here if you want to alter the content
    @Binding var floor: Floor
  
    // floor may be updated from within
    
    var body: some View {
        Text(floor.content)
    }
}

struct ListView: View {
    // If you initialize and hold the viewmodel here use @StateObject
    @StateObject private var vm = ViewModel()

    var body: some View {
        List {
            Button("Alter") {
                vm.alter()
            }
            
            // use the binding syntax here to propagate changes in the child to the parent
            ForEach($vm.floors) { $floor in
                ListBlock(floor: $floor)
            }
        }
    }
}
  • Related