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

问题描述:

我有这样的结构

contentView {
    navigationView{
     foreach {
        NavigationLink(ViewA(id: id))
     }
    }
}

////其中,视图A包含视图出现"中的请求触发器

///where View A contain an request trigger in view Appear

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()
        }

    }

}

}

ViewB与ViewB具有相同的代码, 接收ID,存储并请求api收集数据.

ViewB has the same code than ViewB, Receive id, store, and request api to collect data.

但是viewB列表未刷新. 我还注意到viewB的

But viewB list not being refreshed. i also noticed viewB's

@ObservedObject var model: model = model()

实例化了多个时间

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

Debugging, i found every navigationLink instantie it's destination even before it being trigger. that's not a problem usually,

但是在我的情况下,我感觉ViewB模型正在实例化2次,而我的onApear调用了错误的模型,这是self.objectWillChange.send()为什么不刷新我的视图的原因

but in my case i feel like ViewB model is being instantiate 2 time, and my onApear call the wrong one, reason why self.objectWillChange.send() not refreshing my view

这里有两个问题:

  1. SwiftUI使用的值类型每次通过body都会一次又一次地初始化.
  2. 与#1有关,NavigationLink并不懒惰.
  1. SwiftUI uses value types that that get initialized over and over again each pass through body.
  2. Related to #1, NavigationLink is not lazy.

#1

每次调用ViewA.init(...)时都会实例化一个新的ListObj. ObservedObject 不能@State相同,SwiftUI在整个屏幕生命周期内都会为您仔细跟踪. SwiftUI假定@ObservedObject的最终所有权在使用它的View之上的某个级别存在.

#1

A new ListObj gets instantiated every time you call ViewA.init(...). ObservedObject does not work the same as @State where SwiftUI keeps careful track of it for you throughout the onscreen lifecycle. SwiftUI assumes that ultimate ownership of an @ObservedObject exists at some level above the View it's used in.

换句话说,您几乎应该始终避免使用@ObservedObject var myObject = MyObservableObject()之类的东西.

In other words, you should almost always avoid things like @ObservedObject var myObject = MyObservableObject().

(注意,即使您执行了@State var model = ListObj(),它也会每次都实例化.但是由于它是@State,在调用body之前,SwiftUI会用原始实例替换新实例.)

(Note, even if you did @State var model = ListObj() it would be instantiated every time. But because it's @State SwiftUI will replace the new instance with the original before body gets called.)

除此之外,NavigationLink也不是懒惰的.每次实例化NavigationLink时,都会传递新实例化的ViewA,该实例将实例化ListObj.

In addition to this, NavigationLink is not lazy. Each time you instantiate that NavigationLink you pass a newly instantiated ViewA, which instantiates your ListObj.

因此对于初学者来说,您可以做的一件事是制作一个LazyView来延迟实例化,直到真正调用NavigationLink.destination.body为止:

So for starters, one thing you can do is make a LazyView to delay instantiation until NavigationLink.destination.body actually gets called:

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

现在您可以执行NavigationLink(destination: LazyView { ViewA() }),并且ViewA的实例化将推迟到实际显示destination之前.

Now you can do NavigationLink(destination: LazyView { ViewA() }) and instantiation of ViewA will be deferred until the destination is actually shown.

只需使用LazyView即可解决当前的问题,只要它是层次结构中的顶视图,就像将其放入NavigationView或将其显示时一样.

Simply using LazyView will fix your current problem as long as it's the top view in the hierarchy, like it is when you push it in a NavigationView or if you present it.

但是,这是@ user3441734的注释所在.您真正需要做的是,将#c24的所有权保留在您的View之外的某个地方,因为在#1中已作了解释.

However, this is where @user3441734's comment comes in. What you really need to do is keep ownership of model somewhere outside of your View because of what was explained in #1.