SwiftUI中CoreData存储的DatePicker值无法跨视图显示问题排查
问题排查:CoreData日期保存后无法在视图展示
问题描述
在ProductCardDetails视图中通过DatePicker选择日期并保存至CoreData后,ProductCard视图无法展示该日期数据,无任何内容返回。
相关代码
ProductCard 视图代码
struct ProductCard: View { @Environment (\.managedObjectContext) var managedObjContext // 错误:@FetchRequest未定义变量名,排序键路径与实体属性不匹配 @FetchRequest(entity: Task.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Task.name, ascending: true)]) var body: some View { ScrollView(.horizontal){ HStack{ // 错误:用实体名Task作为循环变量,数据源未指定@FetchRequest结果集 ForEach( Task, id: \.self){ Task in NavigationLink { ProductCardDetails(taskData: Task) } label: { HStack{ // 错误:冗余的self调用 Text(task.self.taskTitle ?? "Error") .font(.title3) .fontWeight(.bold) .foregroundColor(.red) Spacer() Text(task.self.taskDate ?? "Error") .font(.caption) .foregroundColor(.red) }.padding(.horizontal, 10) } } } } } }
ProductCardDetails 视图代码
struct ProductCardDetails: View { let formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-dd-MM HH:mm:ss" return formatter }() @Environment(\.managedObjectContext) var managedObjContext @Environment(\.dismiss) var dismiss var taskData: Task @State private var taskTitle: String = "" var body: some View { let binding = Binding<Date> { formatter.date(from: taskData.taskDate ?? "") ?? Date.now } set: { date, transaction in taskData.taskDate = formatter.string(from: date) } VStack{ Form{ Section(header: Text("Name the task"), footer: Text("Footer of a section can contain extra information about this section")) { TextField("Name your task", text: $taskTitle) .disableAutocorrection(true) } Section(header: Text("When do you need to do this"), footer: Text("")) { DatePicker(selection: binding, in: ...Date()){ Text("Pick time") } } Button("Save and close"){ Task{ do{ // 错误:perform是同步方法,无需await try await managedObjContext.perform { try managedObjContext.save() } dismiss.callAsFunction() } catch{ print(error) } } } }.navigationBarBackButtonHidden() } } }
问题根源及修复方案
1. ProductCard 视图核心修复
- 补全@FetchRequest变量:必须为@FetchRequest声明变量名,才能获取CoreData数据
- 修正数据源与循环变量:用@FetchRequest返回的结果集作为数据源,循环变量避免与实体名重名
- 匹配排序键路径:如果CoreData实体
Task的标题属性是taskTitle,排序键路径需改为\Task.taskTitle - 移除冗余self调用:直接使用
task.taskTitle即可
修复后代码:
struct ProductCard: View { @Environment (\.managedObjectContext) var managedObjContext @FetchRequest(entity: Task.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Task.taskTitle, ascending: true)]) private var tasks: FetchedResults<Task> var body: some View { ScrollView(.horizontal){ HStack{ ForEach(tasks, id: \.self){ task in NavigationLink { ProductCardDetails(taskData: task) } label: { HStack{ Text(task.taskTitle ?? "Error") .font(.title3) .fontWeight(.bold) .foregroundColor(.red) Spacer() Text(task.taskDate ?? "Error") .font(.caption) .foregroundColor(.red) }.padding(.horizontal, 10) } } } } } }
2. ProductCardDetails 视图核心修复
- 绑定taskTitle到实体属性:初始化时从
taskData读取现有标题,修改后同步回实体 - 修正CoreData保存逻辑:
performAndWait确保线程安全,无需await,直接在上下文线程执行保存 - 同步taskTitle到实体:点击保存时将@State变量的值赋值给
taskData.taskTitle
修复后代码:
struct ProductCardDetails: View { let formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "yyyy-dd-MM HH:mm:ss" return formatter }() @Environment(\.managedObjectContext) var managedObjContext @Environment(\.dismiss) var dismiss var taskData: Task @State private var taskTitle: String init(taskData: Task) { self.taskData = taskData self._taskTitle = State(initialValue: taskData.taskTitle ?? "") } var body: some View { let binding = Binding<Date> { formatter.date(from: taskData.taskDate ?? "") ?? Date.now } set: { date, transaction in taskData.taskDate = formatter.string(from: date) } VStack{ Form{ Section(header: Text("Name the task"), footer: Text("Footer of a section can contain extra information about this section")) { TextField("Name your task", text: $taskTitle) .disableAutocorrection(true) } Section(header: Text("When do you need to do this"), footer: Text("")) { DatePicker(selection: binding, in: ...Date()){ Text("Pick time") } } Button("Save and close"){ managedObjContext.performAndWait { taskData.taskTitle = taskTitle do { try managedObjContext.save() dismiss() } catch { print("保存失败:\(error.localizedDescription)") } } } }.navigationBarBackButtonHidden() } } }
3. 额外检查点
- 确认CoreData实体
Task的taskDate属性类型为String(代码中用字符串存储日期),如果是Date类型需修改代码直接存储Date对象 - 确保父视图已正确注入
managedObjectContext,两个视图能获取到同一上下文
内容的提问来源于stack exchange,提问作者Highlyfavoured1
相关产品推荐
相关产品推荐

