Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

tableView:cellForRowAtIndexPath called with nil indexPath after deleting item

I have a rather vanilla UITableView managed by an NSFetchedResultsController to display all instances of a given Core Data entity.

When the user deletes an entry in the table view by swiping over it, tableView:cellForRowAtIndexPath: eventually gets called on my UITableViewController with a nil indexPath. Since I had not expected it to be called with a nil indexPath, the app crashes.

I can work around the crash by checking for that nil value and then returning an empty cell. This seems to work, but I still worry that I may have handled something wrong. Any ideas? Has anybody ever seen tableView:cellForRowAtIndexPath: called with a nil indexPath?

Note that this only happens when the user deletes from the table view by swiping over the cell. When deleting an item using the table view editing mode, it doesn't happen. What would be different between the two ways to delete a cell?

So is it really an OK situation to get a nil indexPath in a table view delegate method?

My view controller code is really standard. Here is the deletion:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
        [self.moc deleteObject:managedObject];
        NSError *error = NULL;
        Boolean success = [self.moc save:&error];
        if (!success) { <snip> }
        // actual row deletion from table view will be handle from Fetched Result Controller delegate
        // [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else { <snip> }   
}

This will lead to the NSFetchedResultsController delegate method being called:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;
    switch(type) {

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

        case NSFetchedResultsChangeInsert: <snip> break;
        case NSFetchedResultsChangeUpdate: <snip> break;
        case NSFetchedResultsChangeMove: <snip> break;
    }
}

And of course, the data source methods are handled by the NSFetchedResultsController, e.g.:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

Many thanks.

like image 965
Jean-Denis Muys Avatar asked Aug 04 '11 12:08

Jean-Denis Muys


1 Answers

It seems like you are deleting the indexPath from table but table data source is not updating. Did you verify the data source udation process by NSFetchedResultsController is correctly updationg the table data source.?

like image 191
Nanda Avatar answered Oct 17 '22 19:10

Nanda