Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is NSFetchedResultsControllerDelegate 'ChangeUpdate' behavior broken?

The docs for NSFetchedResultsControllerDelegate provide the following sample code

- (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:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
            break;

    }

}

When I create a new NSManagedObject, NSFetchedResultsChangeInsert fires (great!). When I change the value of an attribute (used for the cell's title), the NSFetchedResultsChangeUpdate fires. Unfortunately, the new title doesn't automatically display unless I reload the table, section or row. Indeed, if the new name causes the result set to sort differently, then NSFetchedResultsChangeMove fires and all is well since the provided code reloads the entire section.

UITableView has a method reloadRowsAtIndexPaths:withRowAnimation so I tried using this under the NSFetchedResultsChangeUpdate code block. It does indeed work ... but the docs for this specific method read as though I don't need it (notice the last line):

Reloading a row causes the table view to ask its data source for a new cell for that row. The table animates that new cell in as it animates the old row out. Call this method if you want to alert the user that the value of a cell is changing. If, however, notifying the user is not important—that is, you just want to change the value that a cell is displaying—you can get the cell for a particular row and set its new value.

And yes, if I log what is happening, when

[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; 

gets invoked on an NSFetchedResultsChangeUpdate, it is able to retrieve the latest 'name' value and set it in the cell's textLabel. The name is just not rendering in the cell unless I reload it. Even if I simply click the cell the name shows up. Note that to recreate this behavior, you must create a new managed object and then give it a name that causes it to sort FIRST in the NSFetchedResultsController. That way, the NSFetchedResultsChangeMove doesn't fire (which does work since it reloads the section).

Am I missing something or is this expected behavior? The 'discussion' for reloadRowsAtIndexPaths leads me to believe I should be able to simply set the cell's textLabel without reloading the row, section or table.

like image 796
Luther Baker Avatar asked Aug 25 '09 06:08

Luther Baker


1 Answers

You should call [cell setNeedsLayout] or/and [cell setNeedsDisplay] to cause the cell to get refreshed, depending on your cell's implementation.

If you compose the cell of subviews as we usually do, you rely on - layoutSubviews, so you should call [cell setNeedsLayout].

If you draw the cell directly with – drawRect:, you should call [cell setNeedsDisplay].

If you use both composition and drawing, you should call both.

like image 173
an0 Avatar answered Nov 11 '22 17:11

an0