簡體   English   中英

SwiftUI 在其他視圖中更新核心數據屬性時列表未更新

[英]SwiftUI List not updating when core data property is updated in other view

@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()
 }

現在我要詳細查看單個 object:

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

在重命名文檔視圖中:

var document: ScanDocument

另外,一個 function 更新文檔名稱:

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

所有這些代碼都有效。 此打印語句始終打印更新的值:

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

這是主視圖中的列表代碼:

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

但是用戶回到前一個屏幕的地方。 該列表顯示舊數據。 如果我重新啟動應用程序,它會顯示新數據。

向新方向輕推的任何幫助都會有所幫助。

這里有一個類似的問題: SwiftUI List View not updated after Core Data entity updated in another View ,但沒有答案。

NSManagedObject是一種引用類型,因此當您更改其屬性時,您的documents不會更改,因此 state 不會刷新視圖。

這是您回來時強制刷新列表的一種可能方法

  1. 添加新 state
@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()

替代:可能的替代是讓DocumentCell觀察文檔,但沒有提供代碼,因此不清楚里面是什么。 反正你可以試試

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

改變:

var document: ScanDocument

至:

@ObservedObject var document: ScanDocument

Core Data 批量更新不會更新內存中的對象。 之后您必須手動刷新。

批處理操作繞過正常的核心數據操作並直接在底層 SQLite 數據庫(或任何支持您的持久存儲)上操作。 他們這樣做是為了提高速度,但這意味着他們也不會觸發您使用正常獲取請求獲得的所有內容。

您需要執行 Apple 的核心數據批處理編程指南中所示的操作:實施批處理更新 - 執行后更新您的應用程序

原答案類似案例類似案例

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])

嘗試提供此問題的解決方案時,另一個考慮因素與類型定義和您將獲取請求結果強制轉換為ScanDocument object 數組(即[ScanDocument] )有關。

你的代碼行...

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

...正在try強制將您的var documents向下轉換為這種類型 - 一個對象數組。

事實上,一個NSFetchRequest本身返回一個NSFetchRequestResult ,但是您已經定義了您期望從var documents中獲得的類型。

在我的代碼中定義對象數組的類似示例中,我省略了強制向下轉換,然后tryNSFetchRequestResult作為已定義的ScanDocument object 數組返回。

所以這應該工作......

    documents = try managedContext.fetch(fetchRequest)

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

評論 1

所以你可以試試這個...

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

(注:未經測試)

但更重要的是...

評論 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 中的所有核心數據實體默認都是ObservedObject並且也符合Identifiable協議,所以您也可以在列表中省略id參數。

例如...

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM