点击按钮后@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
相关产品推荐
相关产品推荐

