Delete row in table view with fetchedResultController

◇◆丶佛笑我妖孽 提交于 2019-12-04 00:30:38

To do this properly, implement controller:didChangeObject: method, within it make a switch for different change types, within NSFetchedResultsChangeDelete case insert your [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];. Because when you work with data source like NSFetchedResultsController, all changes must come from there and your table only reflects them. Additionally, you can implement controller:willChangeContent and controller:didChangeContent, put [tableView beginUpdates] into willChange and [tableView endUpdates] into didChange.

The problem is - as you've discovered by commenting it out - your last line:

[tableView deleteRowsAtIndexPaths:...

The apple Table View Programming Guide tells you need to do that, but in fact you're not supposed to when using NSFetchedResultsController. The idea of NSFetchedResultsController is that it takes care of updating the UI to keep it in sync with the model so that you don't need to. Hence, if you simply delete the object from the model, the fetched results controller will take care of the rest.

[Update] Whoops, not entirely correct what I said just there. Fetched results controller will not just "take care of the rest" without help. It's delegate will take care of the rest, but someone needs to implement it's delegate. I'm in the habit of including the CoreDataTablewViewController helper class from the Standford CS193p course, which is simply a UITableViewController subclass that implements all of the methods recommended in the documentation of NSFetchedResultsController. Most importantly, it implements the delegate methods that update the table view when the model changes.

So your updated approach is still correct: you should NOT put the deleteRowsAtIndexPaths.. in the code that causes the user to delete them, but let the fetched results controller drive the process, and implement the delegate to actually do it.

(get the helper class here: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2011-fall, and watch lecture 14 to see how to use it)

Kyr's answer is correct, but here is a discrete example of how to delete an NSManagedObject from your database as well as from from the NSFetchedResultsController:

- (void) deleteObjectFromDBAndTable:(NSIndexPath *)indexPath forTable:(UITableView*)tableView
{
    NSLog(@"Deleting object at row %d", indexPath.row);

    // Delete the object from the data source
    NSManagedObject *objToDelete = (NSManagedObject*)[self.fetchedResultsController objectAtIndexPath:indexPath];

    // Delete the object from the database
    [DBHandler deleteObject:objToDelete andSave:YES];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type)
    {
        case NSFetchedResultsChangeDelete:

            [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;
        default:
            break;
    }
}

You need to make sure you don't call [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; yourself. Deleting the object from the context will trigger the NSFetchedResultsController to refresh itself AND the table.

As told by Rhubarb i implemented these following delegates and problem solved

//Fetch delegates implementation from web.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2011-fall (CoreDataTableViewController.zip)
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        self.itemsTableView.beginUpdates()
        beganUpdates = true
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
        switch type {
            case .insert:
                self.itemsTableView.insertSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
            case .delete:
                self.itemsTableView.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
            default:
                break
        }
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        switch type {

            case .insert:
                self.itemsTableView.insertRows(at: [newIndexPath!], with: .fade)
            case .delete:
                self.itemsTableView.deleteRows(at: [indexPath!], with: .fade)
            case .update:
                self.itemsTableView.reloadRows(at: [indexPath!], with: .fade)
            case .move:
                self.itemsTableView.deleteRows(at: [indexPath!], with: .fade)
                self.itemsTableView.insertRows(at: [newIndexPath!], with: .fade)
            default:
                break
        }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    if beganUpdates {
        self.itemsTableView.endUpdates()
    }
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!