如何从 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 中播放歌曲