ios 在其他视图中更新核心数据属性时,SwiftUI列表不更新

5m1hhzi4  于 2023-02-01  发布在  iOS
关注(0)|答案(4)|浏览(179)
@State var documents: [ScanDocument] = []

func loadDocuments() {
    guard let appDelegate =
        UIApplication.shared.delegate as? AppDelegate else {
            return
    }
    
    let managedContext =
        appDelegate.persistentContainer.viewContext
    
    let fetchRequest =
        NSFetchRequest<NSManagedObject>(entityName: "ScanDocument")
    
    do {
        documents = try managedContext.fetch(fetchRequest) as! [ScanDocument]
        print(documents.compactMap({$0.name}))
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }
}

在第一个视图中:

.onAppear(){
     self.loadDocuments()
 }

现在,我将一个对象推入详细视图:

NavigationLink(destination: RenameDocumentView(document: documents[selectedDocumentIndex!]), isActive: $pushActive) {
                    Text("")
                }.hidden()

在重命名文档视图中:

var document: ScanDocument

此外,还有一个更新文档名称的函数:

func renameDocument() {
    guard !fileName.isEmpty else {return}
    document.name = fileName
    try? self.moc.save()
    print(fileName)
    self.presentationMode.wrappedValue.dismiss()
}

所有这些代码都能正常工作。这个print语句总是打印更新后的值:

print(documents.compactMap({$0.name}))

下面是主视图中的列表代码:

List(documents, id: \.id) { item in
     ZStack {
          DocumentCell(document: item)
     }
}

但是用户回到上一个屏幕的地方。列表显示旧数据。如果我重新启动应用程序,它会显示新数据。
任何推动新方向的帮助都会有所帮助。
这里有一个类似的问题:但没有答案。

u3r8eeie

u3r8eeie1#

NSManagedObject是一个引用类型,因此当您更改其属性时,documents不会更改,因此状态不会刷新视图。
下面是一种在返回时强制刷新List的可能方法
1.添加新状态

@State var documents: [ScanDocument] = []
@State private var refreshID = UUID()   // can be actually anything, but unique

1.生成由其标识的列表

List(documents, id: \.id) { item in
     ZStack {
          DocumentCell(document: item)
     }
}.id(refreshID)     // << here

1.返回时更改refreshID,以便强制列表重建

NavigationLink(destination: RenameDocumentView(document: documents[selectedDocumentIndex!])
                               .onDisappear(perform: {self.refreshID = UUID()}), 
                isActive: $pushActive) {
                    Text("")
                }.hidden()

**Alternate:**可能的替代方法是让DocumentCell观察文档,但没有提供代码,所以不清楚里面是什么。无论如何,您可以尝试

struct DocumentCell: View {
   @ObservedObject document: ScanDocument
 
   ...
}
kg7wmglp

kg7wmglp2#

变更

var document: ScanDocument

@ObservedObject var document: ScanDocument
bihw5rsg

bihw5rsg3#

核心数据批量更新不会更新内存中的对象。您必须在之后手动刷新。
批处理操作绕过了普通的Core Data操作,直接在底层的SQLite数据库(或任何支持持久存储的数据库)上操作。它们这样做是为了提高速度,但这意味着它们也不会触发你使用普通获取请求得到的所有东西。
你需要做的事情,如苹果的核心数据批处理编程指南:实现批量更新-执行后更新应用程序
Original answersimilar casesimilar case

let request = NSBatchUpdateRequest(entity: ScanDocument.entity())
request.resultType = .updatedObjectIDsResultType

let result = try viewContext.execute(request) as? NSBatchUpdateResult
let objectIDArray = result?.result as? [NSManagedObjectID]
let changes = [NSUpdatedObjectsKey: objectIDArray]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedContext])
balp4ylt

balp4ylt4#

在尝试提供此问题的解决方案时,另一个考虑因素与类型定义有关,并强制将获取请求结果向下转换为ScanDocument对象数组(即[ScanDocument])。
你的代码...

documents = try managedContext.fetch(fetchRequest) as! [ScanDocument]

...是try强制向下转换你的var documents到这个类型-一个对象数组。
实际上,NSFetchRequest本机返回NSFetchRequestResult,但您已经定义了var documents的类型。
在类似的例子中,我在代码中定义了一个对象数组,我省略了强制向下转换,然后try将尝试返回NSFetchRequestResult作为已经定义的ScanDocument对象数组。
所以这个应该可以...

documents = try managedContext.fetch(fetchRequest)

我还注意到您正在使用SwiftUI List ...

    • 第1号意见**

所以你可以试试这个...

List(documents, id: \.id) { item in
    ZStack {
        DocumentCell(document: item)
    }
    .onChange(of: item) { _ in
        loadDocuments()
    }
}

(Note:未测试)
但更重要的是...

    • 第2号意见**

你不使用@FetchRequest@SectionedFetchRequest视图构建器是有原因的吗?这两种视图构建器都将大大简化你的代码,让生活更有趣。
比如说...

@FetchRequest(entity: ScanDocument.entity(),
              sortDescriptors: [
                NSSortDescriptor(keyPath: \.your1stAttributeAsKeyPath, ascending: true),
                NSSortDescriptor(keyPath: \.your2ndAttributeAsKeyPath, ascending: true)
              ] // these are optional and can be replaced with []
) var documents: FetchedResults<ScanDocument>

List(documents, id: \.id) { item in
    ZStack {
        DocumentCell(document: item)
    }
}

由于SwiftUI中的所有Core Data实体默认为ObservedObject,并且也符合Identifiable协议,因此您也可以在List中省略id参数。
比如说...

List(documents) { item in
    ZStack {
        DocumentCell(document: item)
    }
}

相关问题