无法在部分列表视图中编辑/删除正确的项目 [英] Unable to edit/delete correct item into Section list view

查看:50
本文介绍了无法在部分列表视图中编辑/删除正确的项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有下面的视图,其中包含包含已完成/未完成部分的任务列表,但在尝试编辑列表中的一项时,始终选择该部分的最后一项,甚至不允许删除.它看起来在测试部分内的所有记录都是一条记录时,因此删除幻灯片不起作用,并且在编辑时获取最后一条记录.在完整代码下方,您可以尝试帮助我确定问题所在.

I have the view below which has a list of tasks with completed/incomplete sections but when trying to edit one of the items of the list, the last item of the section is always selected and the delete is not even allowed. It looks when testing that all records inside the section are one single record so the delete slide doesn't work and when editing it get the last record. Below the complete code so you can try to help me identify where the issue is.

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

推荐答案

这是您的 View 的简化版本,因此您可以看到 NSFetchedResultsController 如何完成这项工作.您需要一个 List 才能滑动一行删除.

Here is a simplified version of your View so you can see how a NSFetchedResultsController would do the work. You need a List to be able to swipe a row to delete.

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

这篇关于无法在部分列表视图中编辑/删除正确的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆