SwiftUI编辑Core Data关联Pause实体后DetailView UI不更新求助
SwiftUI + Core Data:编辑关联实体后UI不刷新问题解决
场景与问题
- 基于SwiftUI和Core Data开发应用,
WorkingDay与Pause为一对多关系 - DetailView展示
WorkingDay详情及关联的Pause列表,通过模态视图编辑两者的时间属性 - 核心问题:编辑
Pause实体的开始/结束时间后,Core Data数据已保存,但DetailView的Pause列表不会立即更新;编辑WorkingDay自身属性时UI却能正常刷新
相关代码
DetailView
struct DetailView: View { @Environment(\.dismiss) var dismiss @StateObject var viewModel: ViewModel var body: some View { VStack { List { Section("Pauses") { ForEach(viewModel.model.arrPause, id: \.self) { pause in HStack { Text("Start: \(pause.wrappedStartPause.formatted(date: .omitted, time: .standard))") Text("Finish: \(pause.wrappedFinishPause.formatted(date: .omitted, time: .standard))") } } } } } .onAppear { viewModel.calculatedWorkingTime() } .sheet(isPresented: $viewModel.onChange) { EditSheet(viewModel: viewModel) } } init(model: WorkingDay, persistenceController: PersistenceController) { _viewModel = StateObject(wrappedValue: ViewModel(model: model, persistenceController: PersistenceController.shared)) } }
ViewModel
class ViewModel: ObservableObject { @Published var model: WorkingDay @Published var selectedPause: Pause? @Published var onChange: Bool = false private let persistenceController: PersistenceController init(model: WorkingDay, persistenceController: PersistenceController) { self.model = model self.persistenceController = persistenceController } func update() { if let selectedPause { selectedPause.startPause = ... // 更新后的时间值 selectedPause.finishPause = ... } model.objectWillChange.send() persistenceController.save() } }
已尝试方案
- 为
WorkingDay配置NSFetchedResultsController:未监听Pause实体,无法触发刷新 - 调用
WorkingDay的objectWillChange.send():编辑Pause后调用无效 - 为List添加
.id(UUID()):强制重载但会重建整个视图层级,非理想方案 - 手动重新获取
WorkingDay:未解决问题
解决方案
方案1:手动触发Pause及关联集合的变更通知
Core Data的NSManagedObject默认不会自动向SwiftUI发送变更通知,需手动触发。修改ViewModel的update方法:
func update() { if let selectedPause { selectedPause.objectWillChange.send() // 触发单个Pause的变更通知 selectedPause.startPause = ... // 更新值 selectedPause.finishPause = ... model.arrPause.objectWillChange.send() // 触发WorkingDay关联集合的变更通知 } model.objectWillChange.send() persistenceController.save() }
方案2:用@FetchRequest直接获取关联Pause(推荐)
在DetailView中通过@FetchRequest获取当前WorkingDay关联的所有Pause,让Core Data自动监听变更并刷新UI:
struct DetailView: View { @Environment(\.dismiss) var dismiss @StateObject var viewModel: ViewModel @FetchRequest var pauses: FetchedResults<Pause> var body: some View { VStack { List { Section("Pauses") { ForEach(pauses, id: \.self) { pause in HStack { Text("Start: \(pause.wrappedStartPause.formatted(date: .omitted, time: .standard))") Text("Finish: \(pause.wrappedFinishPause.formatted(date: .omitted, time: .standard))") } } } } } .onAppear { viewModel.calculatedWorkingTime() } .sheet(isPresented: $viewModel.onChange) { EditSheet(viewModel: viewModel) } } init(model: WorkingDay, persistenceController: PersistenceController) { _viewModel = StateObject(wrappedValue: ViewModel(model: model, persistenceController: PersistenceController.shared)) // 初始化FetchRequest,筛选当前WorkingDay关联的Pause _pauses = FetchRequest( entity: Pause.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Pause.startPause, ascending: true)], predicate: NSPredicate(format: "workingDay == %@", model) ) } }
方案3:确保Core Data关系配置正确
检查WorkingDay与Pause的实体关系是否设置了反向关系,且WorkingDay的arrPause属性为默认的Dynamic类型。Core Data会自动管理反向关系的变更传播,确保关联集合的变更能被SwiftUI捕获。
内容的提问来源于stack exchange,提问作者brusherr
相关产品推荐
相关产品推荐

