无法在部分列表视图中编辑/删除正确的项目
Posted
技术标签:
【中文标题】无法在部分列表视图中编辑/删除正确的项目【英文标题】:Unable to edit/delete correct item into Section list view 【发布时间】:2021-04-28 13:23:55 【问题描述】:我在下面的视图中有一个包含已完成/未完成部分的任务列表,但是当尝试编辑列表中的一项时,始终选择该部分的最后一项,甚至不允许删除。它在测试该部分内的所有记录都是一条记录时看起来,因此删除幻灯片不起作用,并且在编辑它时会获取最后一条记录。下面是完整的代码,以便您尝试帮助我确定问题所在。
import SwiftUI
import CoreData
import UserNotifications
struct ListView: View
@Environment(\.presentationMode) var presentationMode
@Environment(\.managedObjectContext) var viewContext
@FetchRequest(fetchRequest: Task.taskList(),animation: .default) private var items: FetchedResults<Task>
@State var isAddFormPresented: Bool = false
@State var taskToEdit: Task?
init(predicate: NSPredicate?, sortDescriptor: NSSortDescriptor)
let fetchRequest = NSFetchRequest<Task>(entityName: Task.entity().name ?? "Task")
fetchRequest.sortDescriptors = [sortDescriptor]
if let predicate = predicate
fetchRequest.predicate = predicate
_items = FetchRequest(fetchRequest: fetchRequest)
UITableViewCell.appearance().backgroundColor = .white
UITableView.appearance().backgroundColor = .white
var body: some View
let data = groupedEntries(self.items)
VStack(alignment: .center)
if data.isEmpty
Spacer()
Image("empty")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 250, height: 250)
.clipShape(Circle())
.overlay(Circle().stroke(Color.pink, lineWidth: 2))
Spacer()
else
List
ForEach(data, id: \.self) (section: [Task]) in
Section(header: Text(section[0].isComplete == false ? "Incomplete" : "Completed")
.font(.body)
.foregroundColor(section[0].isComplete == false ? Color.pink : Color.green)
)
self.completedView(section: section)
.sheet(item: $taskToEdit, onDismiss:
self.taskToEdit = nil
) task in
TaskFormView(
taskToEdit: task,
name: task.name!,
taskDetails: task.taskDetails ?? "",
important: TaskType2(rawValue: task.important ?? "") ?? .none,
urgent: TaskType(rawValue: task.urgent ?? "") ?? .none,
secondaryCategory: Category(rawValue: task.secondaryCategory ?? "") ?? .other,
isComplete: task.isComplete,
dateAdded: task.dateAdded ?? Date()
)
.listStyle(SidebarListStyle())
HStack
Spacer()
Button(action: addTapped)
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 30, height: 30)
.shadow(radius: 20)
.padding(.trailing, 40)
.padding(.bottom, 24)
.accentColor(Color(UIColor.systemRed))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(UIColor.systemYellow).opacity(0.5))
.edgesIgnoringSafeArea(.all)
.sheet(isPresented: $isAddFormPresented)
TaskFormView()
.environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)
func groupedEntries(_ result : FetchedResults<Task>) -> [[Task]]
return Dictionary(grouping: result) (element : Task) in
element.isComplete
.values.sorted() $0[0].dateAdded! < $1[0].dateAdded!
func completedView(section: [Task]) -> some View
ForEach(section, id: \.id) task in
Button(action:
taskToEdit = task
)
HStack
CategoryRowView(category: Category(rawValue: task.secondaryCategory!)!, dateAdded: task.dateAdded!)
VStack(alignment: .leading)
HStack
if task.isComplete != false
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.pink)
.padding(.top, 10)
.padding(.leading, 5)
Text(task.name!)
.font(.system(size: 20, weight: .regular, design: .default))
.foregroundColor(.primary)
.padding(.top, 10)
.padding(.leading, 5)
Spacer()
HStack
Image(systemName: "tag.fill")
.foregroundColor(.secondary)
Text(task.important == "none" ? "Not Important" : "Important")
.font(.system(size: 12, weight: .regular, design: .default))
.foregroundColor(.secondary)
.padding(.vertical)
Text(task.urgent == "none" ? "Not Urgent" : "Urgent")
.font(.system(size: 12, weight: .regular, design: .default))
.foregroundColor(.secondary)
.padding(.vertical)
.padding(.leading, 5)
.padding(.trailing, 2)
Spacer()
.frame(height: 140)
.background(Color(.systemBackground))
.cornerRadius(10)
.shadow(color: .black, radius: 4, x: 0, y: 0)
.padding(.vertical, 5)
.onDelete row in
deleteEntry(row: row, in: section)
func addTapped()
isAddFormPresented.toggle()
private func onReturnTapped()
self.presentationMode.wrappedValue.dismiss()
func deleteEntry(row: IndexSet, in section: [Task])
let task = section[row.first!]
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [task.id!.uuidString])
viewContext.delete(task)
do
try viewContext.save()
catch
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
private let itemFormatter: DateFormatter =
let formatter = DateFormatter()
formatter.dateFormat = "dd MMM"
return formatter
()
【问题讨论】:
谢谢fatihyildizhan :) 你能将 onDelete 修饰符中的行和节打印到控制台吗?我感觉修饰符的位置不正确。 我不能,因为我什至不能滑动记录来激活onDelete来删除记录。 您的代码远不是Minimal Reproducible Example,正文中的分组效率很低,为什么不直接使用NSFetchedResultsController
并让CoreData 完成所有分组/排序工作。此外,您的 onDelete
似乎在部分而不是行上,因此您将获得有关部分的信息而不是任务/行将其向上移动
抱歉无法重现,但我完全分享了我正在处理的代码。我不知道如何在这种情况下使用 NSFetchedResultsController 并且 onDelete 就在构建行的 ForEach 中。
【参考方案1】:
这是您的View
的简化版本,因此您可以了解NSFetchedResultsController
的工作方式。您需要List
才能滑动一行来删除。
import SwiftUI
import CoreData
class TaskListViewModel: ObservableObject
let persistenceController = PersistenceController.previewAware()
@Published var fetchedResultsController: NSFetchedResultsController<Task>?
init()
setupController()
func setupController()
do
fetchedResultsController = try retrieveFetchedController(sortDescriptors: nil, predicate: nil, sectionNameKeyPath: #keyPath(Task.isComplete))
catch
print(error)
func deleteObject(object: Task)
persistenceController.container.viewContext.delete(object)
save()
func save()
do
if persistenceController.container.viewContext.hasChanges
try persistenceController.container.viewContext.save()
objectWillChange.send()
else
catch
print(error)
//MARK: FetchedResultsController setup
extension TaskListViewModel
func retrieveFetchedController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task>
return try initFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
private func initFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) throws -> NSFetchedResultsController<Task>
fetchedResultsController = getFetchedResultsController(sortDescriptors: sortDescriptors, predicate: predicate, sectionNameKeyPath: sectionNameKeyPath)
do
try fetchedResultsController!.performFetch()
return fetchedResultsController!
catch
print( error)
throw error
func getFetchedResultsController(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?, sectionNameKeyPath: String) -> NSFetchedResultsController<Task>
return NSFetchedResultsController(fetchRequest: getEntityFetchRequest(sortDescriptors: sortDescriptors, predicate: predicate), managedObjectContext: persistenceController.container.viewContext, sectionNameKeyPath: sectionNameKeyPath, cacheName: nil)
private func getEntityFetchRequest(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> NSFetchRequest<Task>
let fetchRequest: NSFetchRequest<Task> = Task.fetchRequest()
fetchRequest.includesPendingChanges = false
fetchRequest.fetchBatchSize = 20
if sortDescriptors != nil
fetchRequest.sortDescriptors = sortDescriptors
else
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Task.dateAdded), ascending: false)]
if predicate != nil
fetchRequest.predicate = predicate
return fetchRequest
struct TaskListView: View
@StateObject var vm: TaskListViewModel = TaskListViewModel()
@State var taskToEdit: Task?
var body: some View
if vm.fetchedResultsController?.sections != nil
List
ForEach(0..<vm.fetchedResultsController!.sections!.count)idx in
let section = vm.fetchedResultsController!.sections![idx]
TaskListSectionView(objects: section.objects as? [Task] ?? [], taskToEdit: $taskToEdit, sectionName: section.name).environmentObject(vm)
.sheet(item: $taskToEdit, onDismiss:
vm.save()
)editingTask in
TaskEditView(task: editingTask)
else
Image(systemName: "empty")
struct TaskEditView: View
@ObservedObject var task: Task
var body: some View
TextField("name", text: $task.name.bound)
struct TaskListSectionView: View
@EnvironmentObject var vm: TaskListViewModel
let objects: [Task]
@State var deleteAlert: Alert = Alert(title: Text("test"))
@State var presentAlert: Bool = false
@Binding var taskToEdit: Task?
var sectionName: String
var body: some View
Section(header: Text(sectionName) , content: ForEach(objects, id: \.self)obj in
let task = obj as Task
Button(action:
taskToEdit = task
, label:
Text(task.name ?? "no name")
)
.listRowBackground(Color(UIColor.systemBackground))
.onDelete(perform: deleteItems)
)
private func deleteItems(offsets: IndexSet)
withAnimation
deleteAlert = Alert(title: Text("Sure you want to delete?"), primaryButton: Alert.Button.destructive(Text("yes"), action:
let objs = offsets.map objects[$0]
for obj in objs
vm.deleteObject(object: obj)
//Because the objects in the sections aren't being directly observed
vm.objectWillChange.send()
), secondaryButton: Alert.Button.cancel())
self.presentAlert = true
struct TaskListView_Previews: PreviewProvider
static var previews: some View
NavigationView
TaskListView()
【讨论】:
以上是关于无法在部分列表视图中编辑/删除正确的项目的主要内容,如果未能解决你的问题,请参考以下文章
phpmyadmin:从多个表创建的视图现在 id 字段不是唯一的,无法编辑,更新删除