SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据
Posted
技术标签:
【中文标题】SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据【英文标题】:SwiftUI: Core Data @FetchRequest and List displaying managed objects - losing data on lockscreemn 【发布时间】:2020-04-24 09:22:37 【问题描述】:我有丢失核心数据托管对象中的数据的问题(所有属性,尽管 .objectID)在进入锁屏和返回后变得为零。
我一直在调试这个并发现了这样的问题。
struct SimpleContactsList: View
@FetchRequest var contacts: FetchedResults<Contact>
let companyId: String
init(companyId: String)
self.companyId = companyId
self._contacts = FetchRequest(
entity: Contact.entity(),
sortDescriptors: [
NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:))),
NSSortDescriptor(keyPath: \Contact.createdAt, ascending: true)
],
predicate: NSPredicate(format: "company.id == %@", companyId)
)
var body: some View
List
ForEach(Array(self.contacts.enumerated()), id: \.1.objectID) (i, contact) in
ContactRow(contact: contact)
上面简化了代码,它工作正常。您可以锁定屏幕并返回,并使用有故障的核心数据管理对象重新加载行。
但只是在 ContactRow(contact:contact) 周围添加 ZStack、VStack 会破坏此操作并进入锁屏状态,然后再次返回会导致列表重新加载空托管对象(具有 nil 属性)并且列表为空(如果我使用可选展开contact.name ?? "") 或者如果我使用像contact.name这样的前展开o只会使应用程序崩溃!
所以这打破了
将我的 SimpleContactLists 更改为此会破坏代码,例如添加 ZStack 会阻止从数据库中刷新列表元素?
List
ForEach(Array(self.contacts.enumerated()), id: \.1.objectID) (i, contact) in
ZStack
ContactRow(contact: contact)
.listRowBackground( (i%2 == 0) ? Color("DarkRowBackground") : .white)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.onDelete(perform: self.delete)
更新
contact NSManagedObject 为空,但引用 self.contacts[i] 不是!为什么?
List
ForEach(Array(self.contacts.enumerated()), id: \.1.objectID) (i, contact) in
Button(action:
self.selectedId = self.contacts[i].id!
self.showDetails = true
)
Contact(contact: contact)
//ContactRow(contact: self.contacts[i])
.background(Color.white.opacity(0.01))
.buttonStyle(ListButtonStyle())
.listRowBackground( (i%2 == 0) ? Color("DarkRowBackground") : .white)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.onDelete(perform: self.delete)
更新 2
我有这样的解决方案。它在某些 List-from-FetchResults 案例中有所帮助,但在更复杂的示例中不是所需的解决方案,例如在 .sheet() 中填充托管对象的 List (所以在这里我需要停止展示此类工作表)
@State var theId = UUID()
List
//rows
.id(theId)
.onReceive(appBecomeActivePublisher, perform: _ in
self.theId = UUID()
extension UIApplication
static var didBecomeActivePublisher: AnyPublisher<UIKit.Notification, Never>
NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
.eraseToAnyPublisher()
【问题讨论】:
【参考方案1】:只是猜测,但可能是因为您在 ForEach
中使用了 enumerated()
。如果您关心的是索引,请尝试使用它:
ForEach(0..<contacts.count, id: \.self) i in
// rest appears to be the same
当您获取的结果更新时,这实际上会更新。
只是稍微扩展一下,但 FetchRequest
和 FetchedResults
是 update()
感知的,它们旨在更新 swift ui View
的 body
。 enumerated()
不是,它只是一个简单的集合。因此,当这种情况发生变化时,您的 body
不在乎,它没有订阅它的更改。当FetchedResults
发生变化时,您的body
确实关心它,它订阅了,因此它会自我更新并重新呈现其内容。
【讨论】:
我认为这与此无关,因为我在没有 enumareted() 的情况下也对其进行了测试。有点奇怪的是 self.contacts[i] 已设置,而 contact 未设置。但是只有在这个屏幕上,我有类似的 FetchRequest > List > Row 代码的其他屏幕上,self.objects [i] 中也有空对象。我发现我可以观察到 UIApplication.didBecomeActiveNotification 然后更新@State var theId。并将其设置为 List 上的 .id(theId)。但这就像强制刷新整个列表(UITableView 上的 reloadData()),但这也会失去滚动偏移。但我认为 List 应该自动刷新以上是关于SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI + Core Data - 更新对象(Detail -> DetailEdit)