SwiftUI似乎不会为列表行持久化@StateObjects
,当该行嵌入堆栈或NavigationLink
之类的容器中时。这里有一个例子:
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")
}
}
当您向下滚动列表时,您会看到每出现一个新行都记录了"INIT"
。但向上滚动,您会看到每一行都会再次记录"INIT"
,即使它们已经出现。
现在移除NavigationLink
:
List(0..<40) { _ in
ListRow()
}
@StateObject
的行为与预期一致:每行正好有一个"INIT"
,没有重复。ObservableObject
在视图刷新期间保持不变。
SwiftUI在持久化@StateObjects
时遵循什么规则?在这个例子中,MyObject
可能正在存储重要的状态信息或下载远程资产,那么我们如何确保每行只发生一次(当与NavigationLink
等组合时(?
以下是文档中关于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.
而List
实际上并没有创建新的行实例,而是重用之前创建的并离开屏幕。然而,NavigationLink
每次都会为标签创建新的实例,所以您可以看到这一点。
您的案例可能的解决方案是将NavigationLink
移动到ListRow
:内部
struct ListView: View {
var body: some View {
List(0..<40) { _ in
ListRow()
}
}
}
和
struct ListRow: View {
@StateObject var obj = MyObject()
var body: some View {
NavigationLink(destination: Text("Dest")) { // << here !!
Text("Row")
}
}
}
如果你想在没有导航的地方重用ListRow
,你甚至可以将它们分开
struct LinkListRow: View {
@StateObject var obj = MyObject()
var body: some View {
NavigationLink(destination: Text("Dest")) {
ListRow(obj: obj)
}
}
}