You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:40:15