I want to make a picker use SwiftUI, when I change the value in ChildView, it will not change and called ChildView init.
class ViewModel: ObservableObject {
@Published var value: Int
init(v: Int) {
self.value = v
}
}
struct ChildView: View {
@Binding var value: Int
@ObservedObject var vm = ViewModel(v: 0)
init(value: Binding<Int>) {
self._value = value
print("ChildView init")
}
var body: some View {
VStack {
Text("param value: \(value)")
Text("@ObservedObject bar: \(vm.value)")
Button("(child) bar.value++") {
self.vm.value += 1
}
}
.onReceive(vm.$value) { value = $0 }
}
}
struct ContentView: View {
@State var value = 0
var body: some View {
VStack {
Text("(parent) \(self.value)")
ChildView(value: $value)
}
}
}
But when I remove Text("(parent) \(self.value)")
in ContentView, it seems to be normal.
In general, the described behavior is expected, because source of truth for value
is in parent, and updating it via binding you update all places where it is used. That result in rebuild parent body, so recreate child view.
SwiftUI 2.0
Solution is simple - use state object
struct ChildView: View {
@Binding var value: Int
@StateObject var vm = ViewModel(v: 0) // << here !!
// ... other code
SwiftUI 1.0+
Initialize view model with updated bound value
struct ChildView: View {
@Binding var value: Int
@ObservedObject var vm: ViewModel // << declare !!
init(value: Binding<Int>) {
self._value = value
self.vm = ViewModel(v: value.wrappedValue) // << initialize !!
// .. other code