Reputation: 920
I'm implementing a UITableView with NSFetchedResultsController.
# D5ProductViewController.h
@interface D5ProductViewController : D5ViewControllerAbstract <UITableViewDataSource, UITableViewDelegate, NSFetchedResultsControllerDelegate>
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) D5Product *product;
@property (weak, nonatomic) IBOutlet UITableView *tableView;
- (IBAction)addVariantTapped:(id)sender;
@end
# D5ProductViewController.m
@interface D5ProductViewController ()
@property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;
@end
@implementation D5ProductViewController
- (void)viewLoad {
NSError *error;
[self.fetcedResultsController performFetch:&error];
if (error) {
[self.alerts showError:[error localizedDescription]
:@"Error"];
}
}
#pragma mark - Properties
@synthesize managedObjectContext; // The context is passed from a parent view.
@synthesize fetchedResultsController = _fetchedResultsController;
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Variant"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entityDescription];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"price"
ascending:NO];
[fetchRequest setSortDescriptors:@[sortDescriptor]];
NSString *predicateString = [NSString stringWithFormat:@"productId = '%@' AND isMaster = 0", self.product.identifier];
NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
[fetchRequest setPredicate:predicate];
[fetchRequest setReturnsObjectsAsFaults:NO];
_fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
#pragma mark - NSFetchedResultsControllerDelegate
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
[self.tableView reloadData];
}
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
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 configureCell:(D5VariantTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]
withObject:(D5Variant *)[controller objectAtIndexPath:indexPath]];
break;
case NSFetchedResultsChangeMove:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)configureCell:(D5VariantTableViewCell *)cell
withObject:(D5Variant *)variant {
cell.variant = variant;
}
#pragma mark - UITableViewDataSourceDelegate
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
id sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [self.fetchedResultsController.sections count];
}
#pragma mark - UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
D5VariantTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:D5VariantCellReusableIdentifier
forIndexPath:indexPath];
D5Variant *variant = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.variant = variant;
cell.managedObjectContext = self.managedObjectContext;
return cell;
}
- (IBAction)addVariantTapped:(id)sender {
NSEntityDescription *variantEntityDescription = [NSEntityDescription entityForName:@"Variant"
inManagedObjectContext:self.managedObjectContext];
D5Variant *variant = [[D5Variant alloc] initWithEntity:variantEntityDescription
insertIntoManagedObjectContext:self.managedObjectContext];
variant.productId = self.product.identifier;
variant.price = [self.product.master price];
variant.weight = [self.product.master weight];
[variant markAsInserted];
NSError *error;
[self.fetchedResultsController performFetch:&error];
if (error) {
NSLog(@"%@", error);
}
[self.tableView reloadData];
}
@end
If I understand correctly, having implemented NSFetchedResultsController
's controller:didChangeObject:atIndexPath:forChangeType:newIndexPath
will cause UITableView to ask for a cell for the inserted row, which is configured with the object at the given indexPath
from the fetchedResultsController
.
The thing is that controller:didChangeObject...newIndexPath
is never called when a new object is inserted in the managedObjectContext
.
What am I missing? Do I need to call managedObjectContext
's save method and then [self.tableView reloadData]
.
Thanks in advance!
Upvotes: 3
Views: 684
Reputation: 920
Silly me, I forgot to set the UITableView's delegate:
The viewDidLoad
method, besides setting the bindings, was just performing the fetch. Adding self.tableView.delegate = self;
fixed the problem, BUT, isn't it enough setting the dataSource
outlet in the IB? Which I already did. Why did I need to set the UITableView
's delegate manually? In the case of the NSFetchedResultsController
it needs to be set manually, but isn't it the purpose of the UITableView object having that outlet to just wire it up to the delegate object?
Anyways, setting self.tableView.delegate = self;
fixed the problem.
Upvotes: 1