Here is my test code
import SwiftUI
struct SubView1: View {
@State var data: String
var body: some View {
Text(data)
let _ = addr()
}
func addr() {
let a = withUnsafePointer(to: data) { pointer in
return"\(pointer)"
}
let b = withUnsafePointer(to: self) { pointer in
return"\(pointer)"
}
print("SubView1, \(a), \(b), \(self)")
}
}
struct SubView2: View {
var data: String
var body: some View {
Text(data)
let _ = addr()
}
func addr() {
let a = withUnsafePointer(to: data) { pointer in
return"\(pointer)"
}
let b = withUnsafePointer(to: self) { pointer in
return"\(pointer)"
}
print("SubView2, \(a), \(b), \(self)")
}
}
struct ContentView: View {
@State var data: String = "a"
var body: some View {
let _ = print("ContentView")
SubView1(data: data)
SubView2(data: data)
Button("change") {
data = "b"
print("changed")
}
}
}
Here is what is printed
ContentView
SubView1, 0x000000016ce791a8, 0x000000016ce79170, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
SubView1, 0x000000016ce79260, 0x000000016ce79230, SubView2(data: "a")
changed
ContentView
SubView1, 0x000000016ce7d548, 0x000000016ce7d510, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
SubView1, 0x000000016ce7d600, 0x000000016ce7d5d0, SubView2(data: "b")
In my understanding, @State wrapping is monitoring of variables, and when the wrapped variables change, it will trigger the update of this page. Updating the interface means recreating the body, so all subpages will be recreated.
When I click the button, the data
of ContentView is updated first, which leads to the update of ContentView , and then leads to the reconstruction of SubView1 and SubView2.
Judging from the changed address, it is indeed rebuilt rather than reused.
But the problem is that the data
of ContentView has been updated to "b" at this time, and SubView2 is indeed reinitialized using "b", so why does SubView1 still use "a"?
In addition, I modified another one and added a SubView1 to ContentView
struct ContentView: View {
@State var data: String = "a"
var body: some View {
let _ = print("ContentView")
SubView1(data: data)
SubView2(data: data)
SubView1(data: data)
Button("change") {
data = "b"
print("changed")
}
}
}
The following result was obtained
ContentView
SubView1, 0x000000016d9cd1a8, 0x000000016d9cd170, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
SubView2, 0x000000016d9cd260, 0x000000016d9cd230, SubView2(data: "a")
SubView1, 0x000000016d9cd1a8, 0x000000016d9cd170, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
changed
ContentView
SubView1, 0x000000016d9d1548, 0x000000016d9d1510, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
SubView2, 0x000000016d9d1600, 0x000000016d9d15d0, SubView2(data: "b")
SubView1, 0x000000016d9d1548, 0x000000016d9d1510, SubView1(_data: SwiftUI.State<Swift.String>(_value: "a", _location: Optional(SwiftUI.StoredLocation<Swift.String>)))
It seems that the two SubView1 are the same object?
For the first question.
You have to use Binding and not State in SubView1:
struct SubView1: View {
// @State var data: String
@Binding var data: String
And of course call it appropriately:
SubView1(data: $data)
With State, you keep the state of the var. With Binding, you reference the parameter passed, so it is updated. In SubView2, you pass directly the parameter, so it is used for display.
For your second question, my understanding is that effectively the compiler optimises the views creation. As Subview1 is already created, it can reuse it immediately without creating a new instance.