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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 15:20:37