如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?

Posted

技术标签:

【中文标题】如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?【英文标题】:How to delete Core Data entry from Details View (Edit Item View) in SwiftUI? 【发布时间】:2019-12-05 05:14:06 【问题描述】:

我在 SwiftUI 中制作了一个非常简单的应用程序。使用核心数据的待办事项列表。我可以添加待办事项并将它们与核心数据一起存储。项目显示在 ContentView 的列表中。点击每个项目将我们带到 EditItemView。我设法为每个条目显示正确的数据。从这个视图中,我想删除我在 EditItemView 中看到的这个特定条目。它应该类似于在 ios 上的提醒应用程序中删除列表。删除按钮应删除此特定条目并将我们带回 ContentView。但是……什么都没有发生。我没有收到任何错误,但也没有删除任何内容。

核心数据 在核心数据中,我有 1 个实体:ToDoItem(模块:当前产品模块,代码生成:类定义) 属性: createdAt :日期(今天的默认值) 标题:字符串(默认值 = 空字符串)

这是我目前的代码:

内容视图

import SwiftUI

struct ContentView: View 

    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        entity: ToDoItem.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \ToDoItem.createdAt, ascending: true),
            NSSortDescriptor(keyPath: \ToDoItem.title, ascending: true)
        ]
    ) var toDoItems: FetchedResults<ToDoItem>

    @State private var show_modal: Bool = false

    var body: some View 
        NavigationView 
            List
                ForEach(toDoItems, id: \.self) todoItem in

                    NavigationLink(destination: EditItemView(createdAt: todoItem.createdAt!, title: todoItem.title!)) 

                        ToDoItemView(title: todoItem.title!, createdAt: todoItem.createdAt!)
                    
                

            
            .navigationBarTitle(Text("My List"))
            .navigationBarItems(trailing:
                Button(action: 
                    self.show_modal = true
                ) 
                    Text("Add")
                .sheet(isPresented: self.$show_modal) 
                    AddItemView().environment(\.managedObjectContext, self.managedObjectContext)
                
            )
        
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        return ContentView().environment(\.managedObjectContext, context)
    

添加项目视图

import SwiftUI

struct AddItemView: View 

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var managedObjectContext

    static let dateFormat: DateFormatter = 
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    ()

    @State private var createdAt : Date = Date()
    @State private var showDatePicker = false
    @State private var title = ""

    var body: some View 
        NavigationView 
            ScrollView 

                HStack 
                    Button(action: 
                        self.showDatePicker.toggle()
                    ) 
                        Text("\(createdAt, formatter: Self.dateFormat)")
                    

                    Spacer()
                

                if self.showDatePicker 
                    DatePicker(
                        selection: $createdAt,
                        displayedComponents: .date,
                        label:  Text("Date") 
                    )
                        .labelsHidden()
                


                TextField("to do item", text: $title)
                    .font(Font.system(size: 30))

                Spacer()

            
            .padding()
            .navigationBarTitle(Text("Add transaction"))

            .navigationBarItems(
                leading:
                Button(action: 
                    self.presentationMode.wrappedValue.dismiss()
                ) 
                    Text("Cancel")
                ,

                trailing:
                Button(action: 
                    let toDoItem = ToDoItem(context: self.managedObjectContext)
                    toDoItem.createdAt = self.createdAt
                    toDoItem.title = self.title

                    do 
                        try self.managedObjectContext.save()
                    catch
                        print(error)
                    

                    self.presentationMode.wrappedValue.dismiss()
                ) 
                    Text("Done")
                
            )

        
    


struct AddItemView_Previews: PreviewProvider 
    static var previews: some View 
        AddItemView()
    

EditItemView

import SwiftUI

struct EditItemView: View 

    @Environment(\.managedObjectContext) var managedObjectContext

    static let dateFormat: DateFormatter = 
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    ()

    var createdAt : Date
    var title: String = ""

    @State private var newCreatedAt : Date = Date()
    @State private var showDatePicker = false
    @State private var newTitle = ""

    var body: some View 
        ScrollView 

            HStack 
                Button(action: 
                    self.showDatePicker.toggle()
                ) 
                    Text("\(createdAt, formatter: Self.dateFormat)")
                

                Spacer()
            

            if self.showDatePicker 
                DatePicker(
                    selection: $newCreatedAt,
                    displayedComponents: .date,
                    label:  Text("Date") 
                )
                    .labelsHidden()
            


            TextField(title, text: $newTitle)
                .font(Font.system(size: 30))

        
        .padding()
        .navigationBarTitle(Text("Edit transaction"))
        .navigationBarItems(
            trailing:
            Button(action: 
                print("Delete")

                let deleteToDoItem = ToDoItem(context: self.managedObjectContext)
                self.managedObjectContext.delete(deleteToDoItem)

                do 
                    try self.managedObjectContext.save()
                catch
                    print(error)
                

//              let deleteToDoItem = self.toDoItems[indexSet.first!]
//              self.managedObjectContext.delete(deleteToDoItem)
//
//              do 
//                  try self.managedObjectContext.save()
//              catch
//                  print(error)
//              

            ) 
                Text("Delete")
                    .foregroundColor(.red)
            
        )
    


struct EditItemView_Previews: PreviewProvider 
    static var previews: some View 
        EditItemView(
            createdAt: Date(),
            title: "to do item"
        )
    

ToDoItemView

import SwiftUI

struct ToDoItemView: View 

    static let dateFormat: DateFormatter = 
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    ()

    var title:String = ""
    var createdAt:Date = Date()

    var body: some View 
        HStack
            VStack(alignment: .leading)
                Text(title)
                    .font(.headline)
                Text("\(createdAt, formatter: Self.dateFormat)")
                    .font(.caption)
            
        
    


struct ToDoItemView_Previews: PreviewProvider 
    static var previews: some View 
        ToDoItemView(title: "To do item", createdAt: Date())
    

附:我知道我可以在列表视图中添加 .onDelete。但我想故意让用户更难删除项目。这就是为什么我想将删除按钮移动到详细信息视图。

【问题讨论】:

【参考方案1】:

只需向 EditItemView 添加一个属性

var todoItem: ToDoItem 

同时添加环境对象以关闭视图

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

编辑删除按钮操作

self.managedObjectContext.delete(self.todoItem)
            do 
                try self.managedObjectContext.save()
                self.presentationMode.wrappedValue.dismiss()
            catch
                print(error)
            

最后在 ContentView 上初始化 EditView

ForEach(toDoItems, id: \.self)  todoItem in
EditItemView(todoItem: todoItem)

编辑:

在 EditItemView 中添加:

var todoItem: ToDoItem

并改变这一行:

Text("\(createdAt, formatter: Self.dateFormat)")

收件人:

Text(todoItem.createdAt != nil ? "\(todoItem.createdAt!, formatter: Self.dateFormat)" : "")

还有这一行:

TextField(title, text: $newTitle)

到这里:

TextField(todoItem.title != nil ? "\(todoItem.title!)" : "", text: $newTitle)

感谢 Asperi 在此处提供此解决方案的帮助:Error: Argument type 'Date?' does not conform to expected type 'ReferenceConvertible'

【讨论】:

谢谢。几乎可以工作。该项目已删除,但应用程序崩溃并出现错误:Text("\(todoItem.createdAt!, formatter: Self.dateFormat)") 行上的“线程 1:致命错误:在展开可选值时意外发现 nil”。也许这是我的错,因为我已经从以前的Text("\(createdAt, formatter: Self.dateFormat)") 更改了它,因为现在单独使用 createdAt 不起作用。 我要删除的项目中的 createdAt 有一个值。但我认为问题出在这一行:Text("\(todoItem.createdAt!, formatter: Self.dateFormat)")。这就是我想在之前保存的 Details 视图项的 createdAt 值上显示的方式。但我认为这是不正确的。 如果我使用Text("\(todoItem.createdAt!, formatter: Self.dateFormat)"),它会使应用程序崩溃。如果我使用Text("\(newCreatedAt, formatter: Self.dateFormat)"),它会显示当前日期,而不是在 EditItemView 中保存的日期。也许我把这个初始化放在错误的地方?我把它放在 EditItemView 中的@State private var newTitle = "" 之后 试试这个 ** init(todoItem: ToDoItem) self.todoItem = todoItem _newCreatedAt = State(initialValue: todoItem.createdAt) _newTitle = State(initialValue: todoItem.title!) **跨度> 这给了我以下错误:Value of optional type 'Date?' must be unwrapped to a value of type 'Date', Coalesce using '??' to provide a default when the optional value contains 'nil', Force-unwrap using '!' to abort execution if the optional value contains 'nil' 【参考方案2】:

不知道是不是太晚了,这是我的解决方案:

1.) 我们将调用(或更准确地说,实例化)详细视图的视图命名为“调用者视图”。 在调用者视图中定义一个状态属性以保存对必须删除的核心数据实体的引用:

    @State var entityToDelete: EntityType? = nil

2.) 在详细视图中定义适当的绑定属性到上面的状态属性。

    @Binding var entityToDelete: EntityType?

3.) 使用新属性从调用者视图参数化详细视图的调用(实例化):

CallerView 
    ...
    DetailView(..., $entityToDelete)
    ...

4.) 我加油,在详细视图中,您显示了某个实体的值,并且您可以选择删除它(或类似的东西)。在详细视图中,将 entityToDelete 属性的值设置为必须删除的实体。在单击“删除按钮”后关闭详细视图可能是最佳选择,这取决于您的应用程序语义:

entityToDelete = presentedEntity
    self.presentationMode.wrappedValue.dismiss() // depends on your app logic

5.) 在调用者视图中删除 entityToDelete。这样做的好地方是 .onAppear - 闭包。如果你直接在调用者视图中,你可以有一个警告“在其实现期间修改视图状态......”:

CallerView 


 .onAppear (perform: deleteItem)


...

func deleteItem ()->Void 
        if entityToDelete != nil 
            managedObjectContext.delete(entityToDelete!) 
            try? managedObjectContext.save()
            entityToDelete = nil
        
        
    

最好, 德拉甘

【讨论】:

以上是关于如何从 SwiftUI 的详细信息视图(编辑项目视图)中删除核心数据条目?的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 使用 CoreData 从列表中编辑项目

尝试更新表单提交 SwiftUI 的先前视图

如何将字符串数组从视图模型传递到 swiftUI 中播放歌曲

是否可以在详细视图中编辑 SwiftUI 列表元素?

SwiftUI - 使工具栏的 NavigationLink 使用详细视图

SwiftUI:从工作表导航到新视图