编辑核心数据对象动态列表的详细信息

Posted

技术标签:

【中文标题】编辑核心数据对象动态列表的详细信息【英文标题】:Edit details of a dynamic list of core data object 【发布时间】:2021-02-28 14:41:35 【问题描述】:

我使用 Xcode 默认的 CoreData 模板来构建我的应用程序。 我曾尝试使用 CoreData 并创建这样的实体:

然后我创建了一个 AddItemView,它允许我将项目添加到视图中。

struct AddItemView: View 
@Environment(\.managedObjectContext) var viewContext
@Environment(\.presentationMode) var presentationMode

@State private var notes = ""
@State private var selectedDate = Date()
    
var body: some View 
    NavigationView 
        Form 
            Section 
                TextField("notes", text: $notes)
            
            
            Section 
                DatePicker("", selection: $selectedDate, displayedComponents: .date)
                Text("Your selected date: \(selectedDate)")
            
            
            Section 
                Button("Save") 
                    let newItem = Item(context: self.viewContext)
                    newItem.notes = self.notes
                    newItem.recordDate = self.selectedDate
                    newItem.timestamp = Date()
                    
                    try? self.viewContext.save()
                    self.presentationMode.wrappedValue.dismiss()
                
                
            
        
        .navigationBarTitle("Add Item")
    

它运行良好,可以添加项目。

然后我想单击每个项目以转到详细信息视图。在 DetailView 中,应该有一个编辑按钮,让我可以修改对象。

因此我为此目的创建了三个文件:ItemHost、DetailView、EditorView

项目的导航目的地将转到 ItemHost。

struct ItemListView: View 
@Environment(\.managedObjectContext) private var viewContext

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
    animation: .default)
private var items: FetchedResults<Item>

@State private var showingAddScreen = false

var body: some View 
    NavigationView 
        List 
            ForEach(items, id: \.self)  item in
                NavigationLink(destination: ItemHost(item: item)) 
                    VStack 
                        Text("Item at \(item.timestamp!, formatter: FormatterUtility.dateTimeFormatter)")
                        Text("notes: \(item.notes ?? "")")
                        Text("Item Date: \(item.recordDate!, formatter: FormatterUtility.dateFormatter)")
                    
                
            
            .onDelete(perform: deleteItems)
        
        .toolbar 
            
            ToolbarItem(placement: .navigationBarLeading) 
                #if os(ios)
                EditButton()
                #endif
            

            ToolbarItem(placement: .navigationBarTrailing) 
                Button(action: self.showingAddScreen.toggle()) 
                    Label("Add Item", systemImage: "plus")
                
            
        
        .sheet(isPresented: $showingAddScreen) 
            AddItemView().environment(\.managedObjectContext, self.viewContext)
        
    

ItemHost如下:

struct ItemHost: View 

@Environment(\.editMode) var editMode
@Environment(\.managedObjectContext) var contextView

@State var item: Item

var body: some View 

    NavigationView 
        if editMode?.wrappedValue == .active 
            Button("Cancel") 
                editMode?.animation().wrappedValue = .inactive
                       
        
        
        if editMode?.wrappedValue == .inactive 
            ItemDetailView(item: item)
         else 
            ItemEditor(item: item)
        
    .navigationBarTitle("EditMode Problem")
    .navigationBarItems(trailing: EditButton())     

DetailView只是一个显示细节的视图,没有什么特别的。

struct ItemDetailView: View 

@Environment(\.managedObjectContext) var contextView
@Environment(\.presentationMode) var presentationMode
@State private var showingDeleteAlert = false

let item: Item

var body: some View 
    VStack 
        Text("notes: \(item.notes ?? "")")
        Text("Record Date: \(item.recordDate!, formatter: FormatterUtility.dateFormatter)")
    
    .navigationBarTitle(Text("Item Detail"), displayMode: .inline)
    .alert(isPresented: $showingDeleteAlert) 
        Alert(title: Text("Delete Item"), message: Text("Are you sure?"),
              primaryButton: .destructive(Text("Delete")) 
                self.deleteItem()
            , secondaryButton: .cancel()
        )
    
    .navigationBarItems(trailing: Button(action: 
        self.showingDeleteAlert = true
    ) 
        Image(systemName: "trash")
    )
    


// Problem here
// Can delete the item and go back to list page. But the actual item in the CoreData has not been removed. If I call contextView.save() it will crash.
func deleteItem() 
    contextView.delete(item)
    presentationMode.wrappedValue.dismiss()

EditorView 是这样的:

struct ItemEditor: View 

@Environment(\.presentationMode) var presentation
@State var item: Item
    
var body: some View 
    
    List 
        HStack 
            Text("Notes").bold()
            TextField("Notes", text: $item.notes) // Error
        

        // Error
        DatePicker(selection: $item.recordDate, displayedComponents: .date) 
            Text("Record Date").bold()
        
    
    

这里有几个问题:

    ItemEditor:无法将“Binding”类型的值转换为预期的参数类型“Binding”。我无法选择原始项目对象值并显示它以让用户知道对象内的旧值是什么。

    单击单个导航项后,将不会显示任何内容。我希望它最初(不是编辑模式)然后显示详细视图。如果是编辑模式,则显示编辑器。

我对@binding 以及如何将项目传递到 DetailView 和编辑器感到困惑。编辑器如何将数据保存回 contextView 中的 item 对象?

    对于 ItemDetailView 中的 deleteItem()。它可以删除该项目并显然返回到 ItemListView。但是,当我退出应用程序,然后再次运行时。我发现该项目再次出现,并没有真正删除。

现在单击该项目,它会显示:

【问题讨论】:

那是很多代码,我注意到的东西很少,你已经用“状态”属性包装器标记了你的“项目”实体,核心数据实体是 ManagedObject 子类,而不是结构,所以你应该' t 用“状态”标记它们,而不是使用 ObservedObject,因为它们已经默认确认 ObservableObjects 协议。我想这就是为什么你看不到更新发生的原因。 【参考方案1】:

不要在 Core Data 中使用 @State 来改变 Item。您应该改用@ObservedObject。更新数据后,它将刷新父视图。 请阅读这篇文章: https://purple.telstra.com/blog/swiftui---state-vs--stateobject-vs--observedobject-vs--environme

【讨论】:

以上是关于编辑核心数据对象动态列表的详细信息的主要内容,如果未能解决你的问题,请参考以下文章

保存核心数据对象后,UISplitView 详细信息未更新

ios5 核心数据或首选项列表

iOS Coredata 回滚不起作用

iOS 核心数据 - 编辑对象后没有立即获取任何内容

hgdb数据编辑

核心数据:从详细视图编辑对象