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

点击按钮后@ObservedObject视图未刷新,原因是什么?

问题:修改Core Data Task属性后视图不刷新

问题代码

BoardView

import SwiftUI

struct BoardView: View {
    @Environment(\.managedObjectContext) private var viewContext
    @ObservedObject var board: Board // works fine    
    @State private var newTask: Task?

    var body: some View {
        HStack {
            let taskLists = Arrays.of(taskLists: board.lists)
            ForEach(taskLists, id: \.self) { list in
                VStack {
                    ScrollView {
                        LazyVStack(alignment: .leading) {
                            Text(list.title ?? "")
                            TasksView(taskList: list)
                        }
                    }
                }
            }
        }
    }
}

TasksView

struct TasksView: View {
    @ObservedObject var taskList: TaskList
    
    var body: some View {
        let _ = Self._printChanges()
        let tasks = Arrays.of(tasks: taskList.tasks)
        ForEach(tasks, id: \.self) { task in
            Text(task.name ?? "")
            Button(
                action: { task.name = "XXXX"
                   try? viewContext.save()
                },
                label: { Image(systemName: "arrow.triangle.swap")}
            )
        }
    }
}

Arrays类核心方法

static func of(tasks: NSSet?) -> [Task] {
    Array((tasks as? Set<Task>)?.sorted(by: sortByCreated) ?? [])
}

原因分析与修复方案

1. 首要问题:TasksView未注入viewContext

TasksView中直接调用viewContext.save()但未通过@Environment获取上下文,导致保存操作无法执行(甚至触发编译错误)。

2. 视图未监听Task属性变化

  • @ObservedObject var taskList: TaskList仅监听taskList自身属性的变更,不会自动监听其关联的Task对象的属性变化。
  • ForEach(tasks, id: \.self)使用Task实例本身作为标识,而Core Data对象的哈希值基于对象ID,不会随属性修改而改变,因此ForEach无法检测到单个Task的更新。

修复步骤

步骤1:给TasksView添加viewContext注入

struct TasksView: View {
    @ObservedObject var taskList: TaskList
    @Environment(\.managedObjectContext) private var viewContext // 新增注入
    
    var body: some View {
        let _ = Self._printChanges()
        let tasks = Arrays.of(tasks: taskList.tasks)
        ForEach(tasks, id: \.self) { task in
            Text(task.name ?? "")
            Button(
                action: { 
                    task.name = "XXXX"
                    do {
                        try viewContext.save() // 现在可正常执行保存
                    } catch {
                        print("保存失败:\(error.localizedDescription)")
                    }
                },
                label: { Image(systemName: "arrow.triangle.swap")}
            )
        }
    }
}

步骤2:让视图监听单个Task的变化

有两种可行方案:

方案A:将Task标记为@ObservedObject

在ForEach中为每个Task添加@ObservedObject修饰,使其属性变化触发视图刷新:

ForEach(tasks, id: \.self) { @ObservedObject task in
    Text(task.name ?? "")
    // 按钮代码保持不变
}
方案B:使用FetchRequest(推荐Core Data场景)

通过FetchRequest直接获取当前TaskList下的任务,Core Data会自动发布数据变化通知:

struct TasksView: View {
    @ObservedObject var taskList: TaskList
    @Environment(\.managedObjectContext) private var viewContext
    
    @FetchRequest private var tasks: FetchedResults<Task>
    
    init(taskList: TaskList) {
        self.taskList = taskList
        // 过滤当前TaskList的任务,并按创建时间排序
        _tasks = FetchRequest<Task>(
            sortDescriptors: [NSSortDescriptor(keyPath: \Task.created, ascending: true)],
            predicate: NSPredicate(format: "taskList == %@", taskList)
        )
    }
    
    var body: some View {
        ForEach(tasks) { task in
            Text(task.name ?? "")
            Button(
                action: { 
                    task.name = "XXXX"
                    do {
                        try viewContext.save()
                    } catch {
                        print("保存失败:\(error.localizedDescription)")
                    }
                },
                label: { Image(systemName: "arrow.triangle.swap")}
            )
        }
    }
}

步骤3:优化集合转换方法(可选)

简化Arrays.of(tasks:)的实现,提升可读性:

static func of(tasks: NSSet?) -> [Task] {
    guard let taskSet = tasks as? Set<Task> else { return [] }
    return taskSet.sorted(by: sortByCreated)
}

内容的提问来源于stack exchange,提问作者igr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:06:26