重新创建视图时,如何使 @FetchRequest 的谓词与视图的 @State 保持同步?

问题描述 投票:0回答:1

重新创建视图时,如何使

@FetchRequest
nsPredicate
View
@State
保持同步?重新创建视图时,
@State
变量会暂时初始化为结构体初始化程序(内联或在
init
中)设置的值,然后持久的
@State
值会替换瞬态值。然而,当重新创建视图时,任何
@FetchRequest
nsPredicate
都会重新初始化,但不会替换为
@FetchRequest
的持久状态。

重现步骤

  1. 添加一个名为
    TestCalendar2
    的 xcdatamodel,其中包含一个名为
    Item
    的实体,该实体具有名为
    Date
    timestamp
  2. 属性
  3. 运行应用程序,请注意列表包含一个项目。
  4. 点击“重新创建视图”(强制重新创建视图),并注意列表为空

MRE

import SwiftUI
import CoreData

struct ContentView: View {
    @FetchRequest(sortDescriptors: [], predicate: NSPredicate.init(value: false))
    var items: FetchedResults<Item>
    
    var counter: Int
    
    init(counter: Int) {
        print("**** init")
        self.counter = counter
    }
    
    var body: some View {
        let _ = Self._printChanges()
        List {
            ForEach(items) { item in
                Text(item.timestamp!, style: .date)
            }
        }
        .onAppear() {
            print("**** onAppear")
            let monthYear = Calendar.current.dateComponents([.year, .month], from: Date())
            items.nsPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
                NSPredicate(format: "timestamp >= %@", Calendar.current.date(from: monthYear)! as NSDate),
                NSPredicate(format: "timestamp <= %@", Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: Calendar.current.date(from: monthYear)!)! as NSDate)
            ])
        }
        .onReceive(items.publisher.collect()) { _ in
            print("**** onReceive")
            print(items)
        }
    }
}

@main
struct TestCalendarApp: App {
    @State var counter = 0
    
    let persistenceController = PersistenceController.shared
    
    var body: some Scene {
        WindowGroup {
            Button("Recreate View") { print("****** Recreate View"); counter += 1 }
            ContentView(counter: counter)
            .onAppear() {
                addItem()
            }
        }
        .environment(\.managedObjectContext, persistenceController.container.viewContext)
    }
    
    func addItem() {
        let item = Item(context: persistenceController.container.viewContext)
        item.timestamp = Calendar.current.startOfDay(for: Date())
        try? persistenceController.container.viewContext.save()
    }
}

struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentContainer

    init() {
        container = NSPersistentContainer(name: "TestCalendar2")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        container.viewContext.automaticallyMergesChangesFromParent = true
    }
}

输出

**** init
ContentView: @self, @identity, _items, @16, @40 changed.
**** onReceive
FetchedResults<Item>(results: <__NSArray0 0x1e9c9d208>(

)
, controller: <_TtGC7SwiftUI15FetchControllerT_C13TestCalendar24ItemGVS_14FetchedResultsS2___: 0x600002616ac0>)
**** onAppear
ContentView: @40 changed.
**** onReceive
FetchedResults<Item>(results: <__NSArrayI 0x600002624d80>(
<Item: 0x600002148f50> (entity: Item; id: 0xa3d8f0f393928422 <x-coredata:///Item/p1>; data: {
    timestamp = "2024-04-07 04:00:00 +0000";
})
)
, controller: <_TtGC7SwiftUI15FetchControllerT_C13TestCalendar24ItemGVS_14FetchedResultsS2___: 0x600002616ac0>)

****** Recreate View
**** init
ContentView: @self changed.
**** onReceive
FetchedResults<Item>(results: <__NSArray0 0x1e9c9d208>(

)
, controller: <_TtGC7SwiftUI15FetchControllerT_C13TestCalendar24ItemGVS_14FetchedResultsS2___: 0x600002616ac0>)
swiftui core-data
1个回答
0
投票

一个简单的方法是在主体的顶部设置谓词,然后每当传入

View
的 let 或
View
中声明的状态发生更改时都会更新谓词,例如

    func updatePredicate() {
        let monthYear = Calendar.current.dateComponents([.year, .month], from: Date())
        items.nsPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
                NSPredicate(format: "timestamp >= %@", Calendar.current.date(from: monthYear)! as NSDate),
                NSPredicate(format: "timestamp <= %@", Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: Calendar.current.date(from: monthYear)!)! as NSDate)
            ])
    }

    var body: some View {
        let _ = Self._printChanges()
        let _ = updatePredicate()
        ...
© www.soinside.com 2019 - 2024. All rights reserved.