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
这里有两个问题:
- SwiftUI使用的值类型每次通过
body
都会一次又一次地初始化. - 与#1有关,
NavigationLink
并不懒惰.
- SwiftUI uses value types that that get initialized over and over again each pass through
body
. - 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.