ObervableObject 被多次初始化,并且没有刷新我的视图

Fer*_*and 3 xcode swift swiftui swiftui-navigationlink

我有这样的结构

contentView {
    navigationView{
     foreach {
        NavigationLink(ViewA(id: id))
     }
    }
}
Run Code Online (Sandbox Code Playgroud)

///其中视图A包含一个请求触发器在视图中出现

struct ViewA: View {

    @State var filterString: String = ""

    var id: String!
    @ObservedObject var model: ListObj = ListObj()

    init(id: String) {
        self.id = id
    }

    var body: some View {
        VStack {
            SearchBarView(searchText: $filterString)
            List {
                ForEach(model.items.filter({ filterString.isEmpty || $0.id.contains(filterString) || $0.name.contains(filterString)  }), id: \.id) { item in
                    NavigationLink(destination: ViewB(id: item.id)) {
                        VStack {
                            Text("\(item.name) ")
                        }
                    }
                }
            }

        }
        .onAppear {
            self.model.getListObj(id: self.id) //api request, fill data and call objectWillChange.send()
        }

    }

}

}
Run Code Online (Sandbox Code Playgroud)

ViewB和ViewB的代码一样,Receive id,store,request api来采集数据。

但是 viewB 列表没有被刷新。我也注意到viewB的

@ObservedObject var model: model = model()
Run Code Online (Sandbox Code Playgroud)

被多次实例化

调试时,我发现每个 navigationLink 实例在它被触发之前都是目的地。这通常不是问题,

但就我而言,我觉得 ViewB 模型被实例化了 2 次,而我的 onApear 调用了错误的模型,这是 self.objectWillChange.send() 不刷新我的视图的原因

ars*_*ius 9

这里有两个问题:

  1. SwiftUI 使用每次通过body.
  2. 与#1 相关,NavigationLink不是懒惰。

#1

ListObj每次调用时都会实例化一个新的ViewA.init(...)ObservedObject 工作一样@State在整个屏幕上的生命周期,其中SwiftUI保持它精心为您跟踪。SwiftUI 假设一个的最终所有权@ObservedObject存在于View它所使用的某个级别之上。

换句话说,你应该几乎总是避免诸如@ObservedObject var myObject = MyObservableObject().

(注意,即使你这样做了,@State var model = ListObj()它每次都会被实例化。但是因为它是@StateSwiftUI,它会在body被调用之前用原始实例替换新实例。)

#2

除此之外,NavigationLink就是不偷懒。每次实例化时,NavigationLink都会传递一个新实例ViewA化的ListObj.

所以对于初学者来说,你可以做的一件事就是LazyView延迟实例化,直到NavigationLink.destination.body真正被调用:

// Use this to delay instantiation when using `NavigationLink`, etc...
struct LazyView<Content: View>: View {
    var content: () -> Content
    var body: some View {
        self.content()
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以做NavigationLink(destination: LazyView { ViewA() })并且实例化ViewA将被推迟到destination实际显示。

只要它是层次结构中的顶视图,简单地使用LazyView就可以解决您当前的问题,就像您将其推入 a或呈现它一样。NavigationView

但是,这就是@user3441734 评论的用武之地。您真正需要做的是将所有权保留model在您之外的某个地方,View因为在#1 中进行了解释。


ada*_*086 7

如果您的对象@ObservedObject被多次初始化,那是因为每次状态发生变化时,对象的所有者都会被刷新并重新创建。@StateObject如果您的应用程序是 iOS 14 及以上版本,请尝试使用。它可以防止视图刷新时重新创建对象。 https://developer.apple.com/documentation/swiftui/stateobject

当视图创建自己的 @ObservedObject 实例时,每次丢弃和重绘视图时都会重新创建它。相反,当视图重绘时,@State 变量将保留其值。@StateObject 是 @ObservedObject 和 @State 的组合 - 即使在视图被丢弃和重绘后,ViewModel 的实例也将被保留和重用

SwiftUI 中 ObservedObject 和 StateObject 有什么区别