SwiftUI:视图没有完全更新,当它的@ObservedObject 从父级更新时滞后?
SwiftUI: View does not fully update, lags behind when its @ObservedObject is updated from parent?
我有以下代码:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State var selectedItemMOID:NSManagedObjectID?
var body: some View {
NavigationView {
VStack {
if let selectedItemMOID = selectedItemMOID, let item = viewContext.object(with: selectedItemMOID) as? Item {
ItemView(item: item)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
if let item = addItem() {
selectedItemMOID = item.objectID
}
} label: {
Image(systemName: "plus")
.font(.system(size: 14, weight: .bold))
}
}
}
}
}
private func addItem() -> Item? {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
return newItem
} catch {
return nil
}
}
}
}
struct ItemView: View {
@ObservedObject var item:Item
@State var itemIDString:String = ""
var body: some View {
List {
VStack {
Text(itemIDString)
Text("the item: \(item.objectID)")
}
.onChange(of: item, perform: { newValue in
updateIdString()
})
.onAppear {
updateIdString()
}
}
}
func updateIdString() {
itemIDString = "\(item.objectID)"
}
}
问题:
当我按下加号按钮(右上角)时,ItemView
部分 更新。底部的文本视图立即更新,顶部的滞后。
看到这个行为:
你可以看到顶部的项目 id 总是落后一个(第一次渲染除外)。
因此,例如,点击加号(第一次之后),顶部是 p45,底部是 p46。等等。
为什么置顶文字落后一位?
我也添加了 onChange
,以为可以同步,但没有。没有它,设置第一项后顶部文本永远不会更新。
.onChange(of: item, perform: { newValue in
updateIdString()
})
我需要了解为什么会发生这种情况,这样我才能理解如何正确构建通常使用 @ObservedObject
的视图,以便在将 ObservedObject 设置为新视图时立即更新。
所以,要明确我的问题是如何修复此代码,以便在设置 item
时整个 ItemView
立即更新,以及 为什么会这样 处于较高水平,这样我就可以避免以后出现同样类型的错误。
解决 Asperi 的 方法(感谢您的回答!):
他的回答解决了这个具体案例,但是,我不确定我将如何使用该模式。例如,如果 ItemView
观察到多个对象,比如说一个 Car
和一个 User
实体,那么 .id(car.id)
只会对在 [ 上设置一个新的 Car
做出反应=12=] 而不是对设置新的 User
做出必要的反应。那么,在这种情况下,@ObservedObject
的意义何在?使用建议的 .id(car.id)
它只会对一个而不是另一个做出反应(如果 ItemView
有 6 个 @ObservedObjects... 则更多)。
这是否意味着一个视图应该只有一个@ObservedObject?我不这么认为。我错了吗?
只是想了解如何扩展 id(item.id)
模式(如果它是令人满意的答案)。还没看懂
我觉得是由于reuse/caching,总之渲染优化,试试
List {
// content is here
}
.id(item) // << this one !!
因为你没有使用newValue,改成:
.onChange(of: item, perform: { newValue in
itemIDString = "\(newValue.objectID)"
})
我有以下代码:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State var selectedItemMOID:NSManagedObjectID?
var body: some View {
NavigationView {
VStack {
if let selectedItemMOID = selectedItemMOID, let item = viewContext.object(with: selectedItemMOID) as? Item {
ItemView(item: item)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
if let item = addItem() {
selectedItemMOID = item.objectID
}
} label: {
Image(systemName: "plus")
.font(.system(size: 14, weight: .bold))
}
}
}
}
}
private func addItem() -> Item? {
withAnimation {
let newItem = Item(context: viewContext)
newItem.timestamp = Date()
do {
try viewContext.save()
return newItem
} catch {
return nil
}
}
}
}
struct ItemView: View {
@ObservedObject var item:Item
@State var itemIDString:String = ""
var body: some View {
List {
VStack {
Text(itemIDString)
Text("the item: \(item.objectID)")
}
.onChange(of: item, perform: { newValue in
updateIdString()
})
.onAppear {
updateIdString()
}
}
}
func updateIdString() {
itemIDString = "\(item.objectID)"
}
}
问题:
当我按下加号按钮(右上角)时,ItemView
部分 更新。底部的文本视图立即更新,顶部的滞后。
看到这个行为:
你可以看到顶部的项目 id 总是落后一个(第一次渲染除外)。 因此,例如,点击加号(第一次之后),顶部是 p45,底部是 p46。等等。
为什么置顶文字落后一位?
我也添加了 onChange
,以为可以同步,但没有。没有它,设置第一项后顶部文本永远不会更新。
.onChange(of: item, perform: { newValue in
updateIdString()
})
我需要了解为什么会发生这种情况,这样我才能理解如何正确构建通常使用 @ObservedObject
的视图,以便在将 ObservedObject 设置为新视图时立即更新。
所以,要明确我的问题是如何修复此代码,以便在设置 item
时整个 ItemView
立即更新,以及 为什么会这样 处于较高水平,这样我就可以避免以后出现同样类型的错误。
解决 Asperi 的 方法(感谢您的回答!):
他的回答解决了这个具体案例,但是,我不确定我将如何使用该模式。例如,如果 ItemView
观察到多个对象,比如说一个 Car
和一个 User
实体,那么 .id(car.id)
只会对在 [ 上设置一个新的 Car
做出反应=12=] 而不是对设置新的 User
做出必要的反应。那么,在这种情况下,@ObservedObject
的意义何在?使用建议的 .id(car.id)
它只会对一个而不是另一个做出反应(如果 ItemView
有 6 个 @ObservedObjects... 则更多)。
这是否意味着一个视图应该只有一个@ObservedObject?我不这么认为。我错了吗?
只是想了解如何扩展 id(item.id)
模式(如果它是令人满意的答案)。还没看懂
我觉得是由于reuse/caching,总之渲染优化,试试
List {
// content is here
}
.id(item) // << this one !!
因为你没有使用newValue,改成:
.onChange(of: item, perform: { newValue in
itemIDString = "\(newValue.objectID)"
})