SwiftUI doesn't seem to persist @StateObjects
for list rows, when the row is embedded inside a container like a stack or NavigationLink
. Here's an example:
class MyObject: ObservableObject {
init() { print("INIT") }
}
struct ListView: View {
var body: some View {
List(0..<40) { _ in
NavigationLink(destination: Text("Dest")) {
ListRow()
}
}
}
}
struct ListRow: View {
@StateObject var obj = MyObject()
var body: some View {
Text("Row")
}
}
As you scroll down the list, you see "INIT"
logged for each new row that appears. But scroll back up, and you see "INIT"
logged again for every row - even though they've already appeared.
Now remove the NavigationLink
:
List(0..<40) { _ in
ListRow()
}
and the @StateObject
behaves as expected: exactly one "INIT"
for every row, with no repeats. The ObservableObject
is persisted across view refreshes.
What rules does SwiftUI follow when persisting @StateObjects
? In this example MyObject
might be storing important state information or downloading remote assets - so how do we ensure it only happens once for each row (when combined with NavigationLink
, etc)?
Here is what documentation says about StateObject
:
/// @StateObject var model = DataModel()
///
/// SwiftUI creates a new instance of the object only once for each instance of
/// the structure that declares the object.
and List
really does not create new instance of row, but reuses created before and went offscreen. However NavigationLink
creates new instance for label every time, so you see this.
Possible solution for your case is to move NavigationLink
inside ListRow
:
struct ListView: View {
var body: some View {
List(0..<40) { _ in
ListRow()
}
}
}
and
struct ListRow: View {
@StateObject var obj = MyObject()
var body: some View {
NavigationLink(destination: Text("Dest")) { // << here !!
Text("Row")
}
}
}
You can even separate them if, say, you want to reuse ListRow
somewhere without navigation
struct LinkListRow: View {
@StateObject var obj = MyObject()
var body: some View {
NavigationLink(destination: Text("Dest")) {
ListRow(obj: obj)
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.