【问题标题】:How to delete Core Data entry from Details View (Edit Item View) in SwiftUI?如何从 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。但我想故意让用户更难删除项目。这就是为什么我想将删除按钮移动到详细信息视图。

【问题讨论】:

    标签: ios swift xcode core-data swiftui


    【解决方案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
            }
            
        }
    

    最好, 德拉甘

    【讨论】:

      猜你喜欢
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-06
      • 2019-12-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多