Created
April 10, 2013 22:40
-
-
Save drale2k/5359100 to your computer and use it in GitHub Desktop.
NSFetchedResultsController for UITableViewController
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
// | |
// CoreDataTableViewController.h | |
// | |
// Created for Stanford CS193p Winter 2013. | |
// Copyright 2013 Stanford University. All rights reserved. | |
// | |
// This class mostly just copies the code from NSFetchedResultsController's documentation page | |
// into a subclass of UITableViewController. | |
// | |
// Just subclass this and set the fetchedResultsController. | |
// The only UITableViewDataSource method you'll HAVE to implement is tableView:cellForRowAtIndexPath:. | |
// And you can use the NSFetchedResultsController method objectAtIndexPath: to do it. | |
// | |
// Remember that once you create an NSFetchedResultsController, you CANNOT modify its @propertys. | |
// If you want new fetch parameters (predicate, sorting, etc.), | |
// create a NEW NSFetchedResultsController and set this class's fetchedResultsController @property again. | |
// | |
#import <UIKit/UIKit.h> | |
#import <CoreData/CoreData.h> | |
@interface CoreDataTableViewController : UIViewController <NSFetchedResultsControllerDelegate, UITableViewDataSource, UITableViewDelegate> | |
@property (strong, nonatomic) UITableView *tableView; | |
// The controller (this class fetches nothing if this is not set). | |
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController; | |
// Causes the fetchedResultsController to refetch the data. | |
// You almost certainly never need to call this. | |
// The NSFetchedResultsController class observes the context | |
// (so if the objects in the context change, you do not need to call performFetch | |
// since the NSFetchedResultsController will notice and update the table automatically). | |
// This will also automatically be called if you change the fetchedResultsController @property. | |
- (void)performFetch; | |
// Turn this on before making any changes in the managed object context that | |
// are a one-for-one result of the user manipulating rows directly in the table view. | |
// Such changes cause the context to report them (after a brief delay), | |
// and normally our fetchedResultsController would then try to update the table, | |
// but that is unnecessary because the changes were made in the table already (by the user) | |
// so the fetchedResultsController has nothing to do and needs to ignore those reports. | |
// Turn this back off after the user has finished the change. | |
// Note that the effect of setting this to NO actually gets delayed slightly | |
// so as to ignore previously-posted, but not-yet-processed context-changed notifications, | |
// therefore it is fine to set this to YES at the beginning of, e.g., tableView:moveRowAtIndexPath:toIndexPath:, | |
// and then set it back to NO at the end of your implementation of that method. | |
// It is not necessary (in fact, not desirable) to set this during row deletion or insertion | |
// (but definitely for row moves). | |
@property (nonatomic) BOOL suspendAutomaticTrackingOfChangesInManagedObjectContext; | |
// Set to YES to get some debugging output in the console. | |
@property BOOL debug; | |
@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
// | |
// CoreDataTableViewController.m | |
// | |
// Created for Stanford CS193p Winter 2013. | |
// Copyright 2013 Stanford University. All rights reserved. | |
// | |
#import "CoreDataTableViewController.h" | |
@interface CoreDataTableViewController() | |
@property (nonatomic) BOOL beganUpdates; | |
@end | |
@implementation CoreDataTableViewController | |
#pragma mark - Properties | |
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation | |
{ | |
return YES; | |
} | |
#pragma mark - Fetching | |
- (void)performFetch | |
{ | |
if (self.fetchedResultsController) { | |
if (self.fetchedResultsController.fetchRequest.predicate) { | |
if (self.debug) NSLog(@"[%@ %@] fetching %@ with predicate: %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName, self.fetchedResultsController.fetchRequest.predicate); | |
} else { | |
if (self.debug) NSLog(@"[%@ %@] fetching all %@ (i.e., no predicate)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), self.fetchedResultsController.fetchRequest.entityName); | |
} | |
NSError *error; | |
[self.fetchedResultsController performFetch:&error]; | |
if (error) NSLog(@"[%@ %@] %@ (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), [error localizedDescription], [error localizedFailureReason]); | |
} else { | |
if (self.debug) NSLog(@"[%@ %@] no NSFetchedResultsController (yet?)", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); | |
} | |
[self.tableView reloadData]; | |
} | |
- (void)setFetchedResultsController:(NSFetchedResultsController *)newfrc | |
{ | |
NSFetchedResultsController *oldfrc = _fetchedResultsController; | |
if (newfrc != oldfrc) { | |
_fetchedResultsController = newfrc; | |
newfrc.delegate = self; | |
if ((!self.title || [self.title isEqualToString:oldfrc.fetchRequest.entity.name]) && (!self.navigationController || !self.navigationItem.title)) { | |
self.title = newfrc.fetchRequest.entity.name; | |
} | |
if (newfrc) { | |
if (self.debug) NSLog(@"[%@ %@] %@", NSStringFromClass([self class]), NSStringFromSelector(_cmd), oldfrc ? @"updated" : @"set"); | |
[self performFetch]; | |
} else { | |
if (self.debug) NSLog(@"[%@ %@] reset to nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); | |
[self.tableView reloadData]; | |
} | |
} | |
} | |
#pragma mark - UITableViewDataSource | |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView | |
{ | |
return [[self.fetchedResultsController sections] count]; | |
} | |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section | |
{ | |
return [[[self.fetchedResultsController sections] objectAtIndex:section] numberOfObjects]; | |
} | |
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section | |
{ | |
return [[[self.fetchedResultsController sections] objectAtIndex:section] name]; | |
} | |
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index | |
{ | |
return [self.fetchedResultsController sectionForSectionIndexTitle:title atIndex:index]; | |
} | |
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView | |
{ | |
return [self.fetchedResultsController sectionIndexTitles]; | |
} | |
#pragma mark - NSFetchedResultsControllerDelegate | |
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller | |
{ | |
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) { | |
[self.tableView beginUpdates]; | |
self.beganUpdates = YES; | |
} | |
} | |
- (void)controller:(NSFetchedResultsController *)controller | |
didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo | |
atIndex:(NSUInteger)sectionIndex | |
forChangeType:(NSFetchedResultsChangeType)type | |
{ | |
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) | |
{ | |
switch(type) | |
{ | |
case NSFetchedResultsChangeInsert: | |
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
case NSFetchedResultsChangeDelete: | |
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
} | |
} | |
} | |
- (void)controller:(NSFetchedResultsController *)controller | |
didChangeObject:(id)anObject | |
atIndexPath:(NSIndexPath *)indexPath | |
forChangeType:(NSFetchedResultsChangeType)type | |
newIndexPath:(NSIndexPath *)newIndexPath | |
{ | |
NSLog(@"Changed: %@", anObject); | |
if (!self.suspendAutomaticTrackingOfChangesInManagedObjectContext) | |
{ | |
switch(type) | |
{ | |
case NSFetchedResultsChangeInsert: | |
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
case NSFetchedResultsChangeDelete: | |
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
case NSFetchedResultsChangeUpdate: | |
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
case NSFetchedResultsChangeMove: | |
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; | |
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; | |
break; | |
} | |
} | |
} | |
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller | |
{ | |
if (self.beganUpdates) [self.tableView endUpdates]; | |
NSLog(@"did change"); | |
} | |
- (void)endSuspensionOfUpdatesDueToContextChanges | |
{ | |
_suspendAutomaticTrackingOfChangesInManagedObjectContext = NO; | |
} | |
- (void)setSuspendAutomaticTrackingOfChangesInManagedObjectContext:(BOOL)suspend | |
{ | |
if (suspend) { | |
_suspendAutomaticTrackingOfChangesInManagedObjectContext = YES; | |
} else { | |
[self performSelector:@selector(endSuspensionOfUpdatesDueToContextChanges) withObject:0 afterDelay:0]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment