Created
May 26, 2015 14:11
-
-
Save benvium/5405c4ef14c0b2b394f0 to your computer and use it in GitHub Desktop.
Implements the boilerplate required to update a UITableView based on results from an NSFetchedResultsController
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #import <Foundation/Foundation.h> | |
| #import <UIKit/UIKit.h> | |
| @import CoreData; | |
| typedef NS_ENUM(NSUInteger, FetchResultControllerMode) { | |
| FetchResultsTableViewUpdaterModeMultipleSection, | |
| FetchResultsTableViewUpdaterModeSingleFixedSection | |
| }; | |
| /** | |
| * Implements the boilerplate required to update a UITableView based on results from an NSFetchedResultsController | |
| * | |
| * Can be used to control all sections in a UITableView | |
| * - use mode == FetchResultControllerModeMultipleSection | |
| * - fixedSectionIndex is then ignored. | |
| * | |
| * OR multiple instances of NSFetchedResultsController can be used to each control a single section. | |
| * | |
| * - use mode == FetchResultControllerModeSingleFixedSection | |
| * - fixedSectionIndex is the index of the section this NSFetchedResultsController is using | |
| * | |
| */ | |
| @interface FetchResultsTableViewUpdater : NSObject <NSFetchedResultsControllerDelegate> | |
| @property(nonatomic, weak) UITableView *tableView; | |
| @property(nonatomic, weak) NSFetchedResultsController *fetchedResultsController; | |
| // Used to pass on calls if required TODO: only used in the didChangeObject call so far | |
| @property(nonatomic, weak) id <NSFetchedResultsControllerDelegate> delegate; | |
| /** | |
| * fixedSectionIndex = only used if mode is FetchResultControllerModeSingleFixedSection, in which case the delegates will always pass fixedSectionIndex for the section. | |
| */ | |
| - (instancetype)initWithTableView:(UITableView *)tableView | |
| fetchedResultsController:(NSFetchedResultsController *)fetchedResultsController | |
| mode:(FetchResultControllerMode)mode | |
| fixedSectionIndex:(NSInteger)fixedSectionIndex; | |
| @end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #import "FetchResultsTableViewUpdater.h" | |
| @interface FetchResultsTableViewUpdater () | |
| @property(nonatomic) FetchResultControllerMode mode; | |
| @property(nonatomic) NSInteger fixedSectionIndex; | |
| @end | |
| @implementation FetchResultsTableViewUpdater { | |
| } | |
| - (instancetype)initWithTableView:(UITableView *)tableView | |
| fetchedResultsController:(NSFetchedResultsController *)fetchedResultsController | |
| mode:(FetchResultControllerMode)mode | |
| fixedSectionIndex:(NSInteger)fixedSectionIndex { | |
| self = [super init]; | |
| if (self) { | |
| self.tableView = tableView; | |
| self.fetchedResultsController = fetchedResultsController; | |
| self.fetchedResultsController.delegate = self; | |
| self.mode = mode; | |
| self.fixedSectionIndex = fixedSectionIndex; | |
| } | |
| return self; | |
| } | |
| - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { | |
| NSLog(@"Beginning updates"); | |
| [self.tableView beginUpdates]; | |
| } | |
| - (void)controller:(NSFetchedResultsController *)controller | |
| didChangeObject:(id)anObject | |
| atIndexPath:(NSIndexPath *)indexPath | |
| forChangeType:(NSFetchedResultsChangeType)type | |
| newIndexPath:(NSIndexPath *)newIndexPath { | |
| if (self.mode == FetchResultsTableViewUpdaterModeSingleFixedSection) { | |
| if (indexPath) { | |
| indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:self.fixedSectionIndex]; | |
| } | |
| if (newIndexPath) { | |
| newIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:self.fixedSectionIndex]; | |
| } | |
| } | |
| UITableView *tableView = self.tableView; | |
| switch (type) { | |
| case NSFetchedResultsChangeInsert: { | |
| NSLog(@"Inserting at path %@", newIndexPath); | |
| [tableView insertRowsAtIndexPaths:@[newIndexPath] | |
| withRowAnimation:UITableViewRowAnimationFade]; | |
| break; | |
| } | |
| case NSFetchedResultsChangeDelete: { | |
| NSLog(@"Deleting at path %ld", (long) indexPath.row); | |
| [tableView deleteRowsAtIndexPaths:@[indexPath] | |
| withRowAnimation:UITableViewRowAnimationFade]; | |
| break; | |
| } | |
| case NSFetchedResultsChangeUpdate: | |
| NSLog(@"Updating at path %@", indexPath); | |
| if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) { | |
| [tableView reloadRowsAtIndexPaths:@[indexPath] | |
| withRowAnimation:UITableViewRowAnimationFade]; | |
| } | |
| break; | |
| case NSFetchedResultsChangeMove: | |
| NSLog(@"Moving from path %@ to %@", indexPath, newIndexPath); | |
| [tableView deleteRowsAtIndexPaths:@[indexPath] | |
| withRowAnimation:UITableViewRowAnimationFade]; | |
| [tableView insertRowsAtIndexPaths:@[newIndexPath] | |
| withRowAnimation:UITableViewRowAnimationFade]; | |
| break; | |
| } | |
| // Pass on to self.delegate | |
| if ([((NSObject *) self.delegate) respondsToSelector:@selector(controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:)]) { | |
| [self.delegate controller:controller | |
| didChangeObject:anObject | |
| atIndexPath:indexPath | |
| forChangeType:type | |
| newIndexPath:newIndexPath]; | |
| } | |
| } | |
| - (void)controller:(NSFetchedResultsController *)controller | |
| didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo | |
| atIndex:(NSUInteger)sectionIndex | |
| forChangeType:(NSFetchedResultsChangeType)type { | |
| UITableView *tableView = self.tableView; | |
| if (self.mode == FetchResultsTableViewUpdaterModeMultipleSection) { | |
| if (type == NSFetchedResultsChangeInsert) { | |
| NSLog(@"Inserting section at %lu", (unsigned long) sectionIndex); | |
| NSIndexSet *insertedSection = [NSIndexSet indexSetWithIndex:sectionIndex]; | |
| [tableView insertSections:insertedSection withRowAnimation:UITableViewRowAnimationFade]; | |
| } | |
| else if (type == NSFetchedResultsChangeDelete) { | |
| NSLog(@"Deleting section at %lu", (unsigned long) sectionIndex); | |
| NSIndexSet *deletedSection = [NSIndexSet indexSetWithIndex:sectionIndex]; | |
| [tableView deleteSections:deletedSection withRowAnimation:UITableViewRowAnimationFade]; | |
| } | |
| } | |
| } | |
| - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { | |
| NSLog(@"Ending updates"); | |
| NSLog(@"Fetched %ld Items After Change", (unsigned long) self.fetchedResultsController.fetchedObjects.count); | |
| @try { | |
| [self.tableView endUpdates]; | |
| } | |
| @catch (NSException *ex) { | |
| NSLog(@"RBQFecthResultsTVC caught exception updating table view: %@. Falling back to reload.", ex); | |
| // TODO: no 'reset' method on real NSFetchedResults controller | |
| // [self.fetchedResultsController reset]; | |
| [self.tableView reloadData]; | |
| } | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment