Deleting a row in tableview without deleting Core Data Entity

痞子三分冷 提交于 2019-12-11 08:38:55

问题


I have a table view backed by NSFetchedResultsController. The situation is as follows. I have implemented "swipe to delete" a row. Delegate methods of NSFetchedResultsController and those of table view's are called. But the thing is, I don't want to delete a Core Data Entity, I just want to mark its boolean flag since I need this entity in other parts of application, and want table view to delete row with animation. However, according to error messages it is against logic like if you are updating an entity, update a row, not delete it. Is there any elegant way to achieve it? Or the only option is reload the table?
Implementation of swipe to delete

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {

   if (editingStyle == UITableViewCellEditingStyleDelete) {
      Issue *issue = [self.fetchedResultsController objectAtIndexPath:indexPath];
      [issue setHasBeenDeleted:[NSNumber numberWithBool:YES]];
      [issue setPurchased:[NSNumber numberWithBool:NO]];
      [self.managedObjectContext saveToPersistentStore:nil];
   }
}

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

...

        case NSFetchedResultsChangeUpdate:

                             [self.tableView deleteRowsAtIndexPaths:@[indexPath]
                              withRowAnimation:UITableViewRowAnimationAutomatic];
        break;

NSFetchedResultsControllerDelegate

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
  [self.tableView endUpdates];
}

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.tableView beginUpdates];
}

Error message

CoreData: error: Serious application error. An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)


回答1:


In the fetch request you are giving to your FRC, include this boolean value as a part of your predicate. For example, [NSPredicate predicateWithFormat:@"shouldDisplay = YES"]. Then whenever you want to remove are particular object, just set your shouldDisplay (or whatever you call it) to NO, and you will receive an FRC delegate callback for controller: didChangeObject: with a NSFetchedResultsChangeDelete change type. In this callback, you will do the following.

NSIndexPath *indexPath = [controller indexPathOfObject:anObject];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

Your object will remain in core data, but it will be filtered out of this particular table view because of your predicate on the boolean flag.

EDIT: You also need to implement two of the FRC delegate methods to notify your table view to begin and end updates. This will correct the error you are receiving. Like so:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {

    [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {


    [self.tableView endUpdates];
}



回答2:


I couldn't get Pat Goley's solution to work, though perhaps I didn't implement it properly. After some experimentation, the solution I came up with is contained in a single callback method:

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle != .Delete { return }
    let obj = fetchedResultsController.objectAtIndexPath(indexPath) as! Entity
    obj.needsDeletion = true
    try! managedObjectContext.save()
    try! fetchedResultsController.performFetch()
    tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}

This has the disadvantage that performFetch() must be called, but without it, it's crash city. For very large recordsets, an approach similar to Pat's is probably best.



来源:https://stackoverflow.com/questions/20381884/deleting-a-row-in-tableview-without-deleting-core-data-entity

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!