I have a simple child view that contains a text view.
Changing the state of my parent view should change that of my child, but it does not happen.
See the example code below. The Text (green) in ContentView is updated correctly when the button is pressed, but not the child view (Orange).
Why?
struct ContentView: View {
@State var msg: String = ""
var body: some View {
VStack {
Button(action: {
self.msg = "Hallo World"
}, label: { Text("Say Hallo")})
ChildView(msg: msg).padding().background(Color.orange)
Text(msg).padding().background(Color.green)
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct ChildView: View {
@State var msg: String
var body: some View {
Text(msg)
}
}
Your Button, defined inside ContentView, is only changing the content of the property within the ContentView. You've passed a copy of that to ChildView, so it has a separate value, stored elsewhere in memory. Thus changing ContentView.msg won't alter the value of ChildView.msg.
I suspect that if your ChildView's msg property were not an @State value, then it would be recreated. The way SwiftUI works in regard to state is this:
- Cleans the 'touched' state on all @State variables.
- Invokes body on a view.
- Looks at the 'touched' state on the @State variables.
- If a state property was accessed, it's marked, so that when that state is modified, it triggers another body call.
In your ContentView, you're accessing your local @State property, so changes to that property now trigger a redraw. In the ChildView, you're accessing a different @State property. Now, when ContentView.body is invoked a second time, it generates a view containing a new ChildView with a new value (as expected). Then SwiftUI goes to use these new values to patch up the real UI types (UILabel, etc). I suspect that, because the original ChildView is using its own @State property, and SwiftUI hasn't seen it being modified, then SwiftUI is skipping that update—after all, to SwiftUI this is 'just a view' and thus could be small and efficient or a bloated monster that takes a second to redraw. It'll do everything it can to avoid updating the view hierarchy.
So I suspect that making ChildView be a POD (Plain Old Data) type, with no @State variables, would in fact cause it to redraw, since SwiftUI has no other means of seeing the change.
Alternatively, note that the Text view is Equatable (also note: its View conformance is in an extension—which should tell you something about what Text really is). If you make your ChildView conform to Equatable then you may find that SwiftUI can now tell the difference between two instances, and will update the live view content accordingly.