NSFetchedResultsController: Moved Objects Sometimes Reported as Updated
In some situations, an instance of NSFetchedResultsController
may report moved objects using a NSFetchedResultsChangeUpdate
change notification instead of NSFetchedResultsChangeMove
.
A fetched results controller only sends object change notifications tagged as NSFetchedResultsChangeMove
when the original index path is different from the new index path. It's possible that a series of object changes can result in an object being moved (such as moving to a new section), yet its relative index path remain the same (if other object changes happened in objects that appear before the moved object). This scenario will result in a NSFetchedResultsChangeUpdate
notification being sent to the delegate.
A possible workaround is to maintain an extra (non modeled) instance variable in your object that indicates when an object has changed section (due to a change in its property that determines the section). For example, you might declare a custom class as follows:
@interface MyClass : NSManagedObject |
{ |
BOOL _changedSection; |
} |
|
// The modeled property that determines the object's section. |
@property (nonatomic, retain) NSString *theSectionKey; |
|
// The unmodeled property that tracks if a section was recently changed. |
@property BOOL changedSection; |
|
@end |
The corresponding implementation might be:
@synthesize changedSection=_ changedSection; |
|
- (void)setTheSectionKey:(id)value |
{ |
if (value != theSectionKey) { |
changedSection = YES; |
} |
|
[self willChangeValueForKey:@"theSectionKey"]; |
[self setPrimitiveTheSectionKey:value]; |
[self didChangeValueForKey:@"theSectionKey"]; |
} |
You can then use this property to determine if a change flagged as NSFetchedResultsChangeUpdate
should actually be treated as a NSFetchedResultsChangeMove
by implementing controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
as illustrated in this example:
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { |
|
UITableView *tableView = self.tableView; |
|
// This is the workaround. |
MyClass *myInstance = (MyClass *)anObject; |
if ( (NSFetchedResultsChangeUpdate == type) && ([myInstance changedSection]) ) { |
[myInstance setChangedSection:NO]; |
type = NSFetchedResultsChangeMove; |
newIndexPath = indexPath; |
} |
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:(ExpenseCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath]; |
break; |
case NSFetchedResultsChangeMove: |
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; |
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; |
break; |
} |
} |
Copyright © 2018 Apple Inc. All rights reserved. Terms of Use | Privacy Policy | Updated: 2009-10-09