Skip to content

Instantly share code, notes, and snippets.

@MrRooni
Created February 19, 2013 19:16
Show Gist options
  • Save MrRooni/4988922 to your computer and use it in GitHub Desktop.
Save MrRooni/4988922 to your computer and use it in GitHub Desktop.
UITableView and NSFetchedResultsController: Updates Done Right
@interface SomeViewController ()
// Declare some collection properties to hold the various updates we might get from the NSFetchedResultsControllerDelegate
@property (nonatomic, strong) NSMutableIndexSet *deletedSectionIndexes;
@property (nonatomic, strong) NSMutableIndexSet *insertedSectionIndexes;
@property (nonatomic, strong) NSMutableArray *deletedRowIndexPaths;
@property (nonatomic, strong) NSMutableArray *insertedRowIndexPaths;
@property (nonatomic, strong) NSMutableArray *updatedRowIndexPaths;
@end
@implementation SomeViewController
#pragma mark - NSFetchedResultsControllerDelegate methods
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
if (type == NSFetchedResultsChangeInsert) {
if ([self.insertedSectionIndexes containsIndex:newIndexPath.section]) {
// If we've already been told that we're adding a section for this inserted row we skip it since it will handled by the section insertion.
return;
}
[self.insertedRowIndexPaths addObject:newIndexPath];
} else if (type == NSFetchedResultsChangeDelete) {
if ([self.deletedSectionIndexes containsIndex:indexPath.section]) {
// If we've already been told that we're deleting a section for this deleted row we skip it since it will handled by the section deletion.
return;
}
[self.deletedRowIndexPaths addObject:indexPath];
} else if (type == NSFetchedResultsChangeMove) {
if ([self.insertedSectionIndexes containsIndex:newIndexPath.section] == NO) {
[self.insertedRowIndexPaths addObject:newIndexPath];
}
if ([self.deletedSectionIndexes containsIndex:indexPath.section] == NO) {
[self.deletedRowIndexPaths addObject:indexPath];
}
} else if (type == NSFetchedResultsChangeUpdate) {
[self.updatedRowIndexPaths addObject:indexPath];
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex
forChangeType:(NSFetchedResultsChangeType)type
{
switch (type) {
case NSFetchedResultsChangeInsert:
[self.insertedSectionIndexes addIndex:sectionIndex];
break;
case NSFetchedResultsChangeDelete:
[self.deletedSectionIndexes addIndex:sectionIndex];
break;
default:
; // Shouldn't have a default
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
NSInteger totalChanges = [self.deletedSectionIndexes count] +
[self.insertedSectionIndexes count] +
[self.deletedRowIndexPaths count] +
[self.insertedRowIndexPaths count] +
[self.updatedRowIndexPaths count];
if (totalChanges > 50) {
[self.tableView reloadData];
return;
}
[self.tableView beginUpdates];
[self.tableView deleteSections:self.deletedSectionIndexes withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertSections:self.insertedSectionIndexes withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView deleteRowsAtIndexPaths:self.deletedRowIndexPaths withRowAnimation:UITableViewRowAnimationLeft];
[self.tableView insertRowsAtIndexPaths:self.insertedRowIndexPaths withRowAnimation:UITableViewRowAnimationRight];
[self.tableView reloadRowsAtIndexPaths:self.updatedRowIndexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
// nil out the collections so their ready for their next use.
self.insertedSectionIndexes = nil;
self.deletedSectionIndexes = nil;
self.deletedRowIndexPaths = nil;
self.insertedRowIndexPaths = nil;
self.updatedRowIndexPaths = nil;
}
#pragma mark - Overridden getters
/**
* Lazily instantiate these collections.
*/
- (NSMutableIndexSet *)deletedSectionIndexes
{
if (_deletedSectionIndexes == nil) {
_deletedSectionIndexes = [[NSMutableIndexSet alloc] init];
}
return _deletedSectionIndexes;
}
- (NSMutableIndexSet *)insertedSectionIndexes
{
if (_insertedSectionIndexes == nil) {
_insertedSectionIndexes = [[NSMutableIndexSet alloc] init];
}
return _insertedSectionIndexes;
}
- (NSMutableArray *)deletedRowIndexPaths
{
if (_deletedRowIndexPaths == nil) {
_deletedRowIndexPaths = [[NSMutableArray alloc] init];
}
return _deletedRowIndexPaths;
}
- (NSMutableArray *)insertedRowIndexPaths
{
if (_insertedRowIndexPaths == nil) {
_insertedRowIndexPaths = [[NSMutableArray alloc] init];
}
return _insertedRowIndexPaths;
}
- (NSMutableArray *)updatedRowIndexPaths
{
if (_updatedRowIndexPaths == nil) {
_updatedRowIndexPaths = [[NSMutableArray alloc] init];
}
return _updatedRowIndexPaths;
}
@end
@tonnylitao
Copy link

another fork

fix iOS8/9 coredata context merge issue
UITableView UICollectionView both works

https://gist.github.com/TonnyTao/314fe120ceaf702c0aa9

@MrRooni
Copy link
Author

MrRooni commented Sep 15, 2016

Hey folks. Wow, sorry that I never saw these messages. This code is free to use for whoever finds it useful. There is no license for it, so go nuts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment