iRebel_85
iRebel_85

Reputation: 749

Updating UITableView that is managed by NSFetchedResultsController and has 1 extra cell in the first section

Master Detail Application -

I have a UITableViewController that is managed by an NSFetchedResultsController and its delegate methods. I also have one extra cell in the first section of the tableview that has a UIWebView in it that displays an embedded video. This cell is not part of the NSFetchedResultsController. I add it inside of an IF statement when -tableView cellForRowAtIndexPath is called, that checks to see if it's the first section and first row. Everything works great for the most part. I can select a row and display it in the DetailViewController, I can delete items from the tableView, I can change the information in the DetailViewControllerand the tableViewCell labels update with out any problem.

MY ISSUE

The only time I have an issue with the way it is setup is, when I delete the last object from the first section in the tableView.

THE ERROR

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 (3) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 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)

CODE

numberOfRowsInSection

This is where I check to see if the video is available and if the section is 0 return 1 extra row. else return the object count from the NSFetchedResultsController. I'm assuming this is where my issue is. However, I don't know how to fix it. Because I have it managed by the NSFetchedResultsController delegate methods is there any thing I can do inside of the "type" in the switch statement?

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section == 0 && self.videoInfoReceived == YES) {
        id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
        return [secInfo numberOfObjects] + 1;
    } else {
        id <NSFetchedResultsSectionInfo> secInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
        return [secInfo numberOfObjects];
    }

}

NSFetchedResultsController Delegates

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"didChangeSection - ChangeInsert");
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"didChangeSection - ChangeDelete");
            break;
    }
}

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

    switch(type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"didChangeObject - ChangeInsert");
            break;

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"didChangeObject - ChangeDelete");
            break;

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            NSLog(@"didChangeObject - ChangeUpdate");
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"didChangeObject - ChangeMove");
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];
}

Upvotes: 2

Views: 1479

Answers (2)

Wain
Wain

Reputation: 119031

You need more conditional code to check if any index path you're sent is for the first (zero) section. If it is, the FRC is being given the 'wrong' row number because you have told the table view one row count and the FRC understands a different one.

Basically, everywhere you receive an index path from the table view, check the section, if it == 0, create a new index path with the same section and row - 1 and use that to access the objects in the FRC.

Conversely, when you receive an index path from the FRC you need row + 1 before you can use it to ask the table view to insert/delete/move.

Upvotes: 1

jaredsinclair
jaredsinclair

Reputation: 12687

In your question you state:

when I delete the last object from the first section in the tableView

Which would lead me to expect the exception to state the following (emphasis added):

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 (0) must be equal to the number of rows contained in that section before the update (1), 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)

Because the exception states that the row count is increasing from 2 to 3 without any rows being added, I think that your error is down in the model layer and not in the table view code. Your table view code looks great to me.

Upvotes: 0

Related Questions