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

时间:2019-12-05 05:14:06

标签: ios swift xcode core-data swiftui

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

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

这是我到目前为止的代码:

ContentView

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)
    }
}

AddItemView

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())
    }
}

P.s。我知道我可以在列表视图中添加.onDelete。但是,我想使用户故意删除条目变得更加困难。这就是为什么我要将“删除”按钮移到详细信息视图的原因。

2 个答案:

答案 0 :(得分: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'

答案 1 :(得分:0)

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

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

    @State var entityToDelete: EntityType? = nil

2.) 在 Detail View 中定义适当的绑定属性到上面的 state 属性。

    @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
        }
        
    }

最好的, 德拉甘