问题描述
我有下面的视图,其中包含包含已完成/未完成部分的任务列表,但在尝试编辑列表中的一项时,始终选择该部分的最后一项,甚至不允许删除.它看起来在测试部分内的所有记录都是一条记录时,因此删除幻灯片不起作用,并且在编辑时获取最后一条记录.在完整代码下方,您可以尝试帮助我确定问题所在.
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()
}
}
}
这篇关于无法在部分列表视图中编辑/删除正确的项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!