1

我有丢失核心数据托管对象中的数据的问题(尽管.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)
    }

从 Core Data 加载的空列表

更新

联系人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()
    }
}
4

1 回答 1

0

只是一个猜测,但可能是因为您enumerated()ForEach. 如果您关心的是索引,请尝试使用它:

ForEach(0..<contacts.count, id: \.self) { i in
    // rest appears to be the same
}

当您获取的结果更新时,这实际上会更新。

只是为了扩展一点,但FetchRequest因此FetchedResultsupdate()知道,它们是为了更新 swift uiViewbodyenumerated()不是,它只是一个简单的集合。所以当它发生变化时,你body不在乎,它没有订阅它的变化。当FetchedResults发生变化时,你body 关心它,它会订阅,因此它会自我更新并重新呈现其内容。

于 2020-04-24T11:26:33.917 回答