Moving and updating UITableViewCell and NSFetchedResultsController simultaneously

I have a simple table view with 1 section and 2 rows. I am using NSFetchedResultsController to synchronize a table with CoreData. I am making changes to one of the lines on the CD that causes the table view cell to be updated and moved. The problem is that when cellForRowAtIndexPath: is called during NSFetchedResultsChangeUpdate , the wrong cell is returned (this makes sense b / c, the cells have not yet been moved). Therefore, the wrong cell is updated with updated data. After that, the NSFetchedResultsChangeMove message NSFetchedResultsChangeMove processed so that the cells trade in places (neither the contents of the cells are updated since it was just called). As a result, both cells reflect data from a recently updated CD object. A problem reloading the table fixes the problem. I am running iOS 6. In other words, if the cell at index 0 represents object A, and index 1 represents object B, and I update object A to A 'so that there are 2 cells in reverse order, the result is that I see 0: A '1: A when I would expect 0: B, 1: A'.

 - (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: //the wrong cell is updated here [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; break; case NSFetchedResultsChangeMove: [tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath]; //this code produces errors too //[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; //[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; } } 
+6
source share
4 answers

I would recommend you take a look at this blogpost

It is easy to fix the error. Just rely on the UITableView behavior described above and replace the configureCell: atIndexPath: call with the reloadRowsAtIndexPaths: withRowAnimation: method, which will automatically do the right thing:

 case NSFetchedResultsChangeUpdate: [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; break; 
+4
source

Decision:

 [self configureCell:(SyncCell*)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:newIndexPath ? newIndexPath : indexPath]; 

Use the new index path when it is delivered during the upgrade. And then use delete and insert instead of move. I would still like to know if anyone else has any data.

+6
source

When calling configureCell, find indexPath based on the passed object. Both indexPath and newIndexPath are currently unreliable. For instance:

 case NSFetchedResultsChangeUpdate: myPath = [controller indexPathForObject:anObject]; if (myPath) { [self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:myPath]; } break; 
+3
source

The only solution that worked for me: Thomas Worrall Blog: Reordering Strings in a UITableView with Master Data

First of all, you need to create an attribute in your NSManagedObject in order to hang the last order of your object.

 @interface MyEntity : NSManagedObject @property (nonatomic, retain) NSNumber * lastOrder; @end 

Then declare the property in your ViewController.m:

 @interface ViewController () @property (nonatomic) BOOL isReordering; @end 

In the tableView: moveRowAtIndexPath: toIndexPath: method , control the changes in the temporary array:

 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { _isReordering = YES; NSMutableArray *arrayNewOrder = [[_fetchedResultsController fetchedObjects] mutableCopy]; MyEntity *myManagedObject = [arrayNewOrder objectAtIndex:fromIndexPath.row]; [arrayNewOrder removeObjectAtIndex:fromIndexPath.row]; [arrayNewOrder insertObject:myManagedObject atIndex:toIndexPath.row]; for (int i=0; i < [arrayNewOrder count]; i++) { myManagedObject = [arrayNewOrder objectAtIndex:i]; myManagedObject.lastOrder = [NSNumber numberWithInt:i]; } _isReordering = NO; NSError *error; BOOL success = [self.fetchController performFetch:&error]; if (!success) { // Handle error } success = [[self managedObjectContext] save:&error]; if (!success) { // Handle error } } 

Thomas explained how to fetch and maintain context:

I'm not quite sure why sampling is needed first, but he recorded a ton of crazy mistakes when I did this!

The last trick handles the delegate method NSFetchedResultsController : didChangeObject: atIndexPath: forChangeType: newIndexPath: in particular in NSFetchedResultsChangeMove:

 case NSFetchedResultsChangeMove: { if (!_isReordering) { [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; } break; } 

It worked for me! Hope this helps you!

+1
source

All Articles