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