Reputation: 489
I have UITableViewController
table and fetched rows from Core Data using NSFetchedResultsController
. My tableView
is responsible for layering data. Eatch row position represents layer (at zero row highest layer, n - row lowest). Lets say we have object with name Layers *layers
. When I create new layer, it needs to go to 1st row (indexPath.row == 0
) in the table and needs to get layers.layer.position = 0
. When I create new layer or some other changes are made (move, delete) I need to refresh Core data object layers
(each layer needs to get new position value). But if I refresh layers position by calling [self updateLayersPosition]
method iside of controllerDidChangeContent
method it goes to infinite loop because everytime on every old layer value change it returns to the same controllerDidChangeContent
method again and again. So the question is how to "bind" table row or fetched controllers indexPath.row
with layers.layer.position
? Maybe I can some how stop fetchedresultscontroller
from fetching, do updates and then start fetching again? Or maybe there is some other way to do it?
Here is the code:
-(void)updateLayers{
for (int i = 0; i < [self.tableView numberOfRowsInSection:0]; i++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:i
inSection:0];
((Layer *)[self.fetchedResultsController objectAtIndexPath:indexPath]).layer.position = i;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[self updateLayers];
[self.tableView endUpdates];
}
Upvotes: 2
Views: 625
Reputation: 7944
All the willChange
/ didChange
methods in NSFetchedResultsControllerDelegate
are called after you modify Core Data objects. So if you modify any NSManagedObject
inside controllerDidChangeContent:
, it will trigger controllerDidChangeContent:
again, leading to the infinite loop.
You can think about these delegate callbacks as a controller saying "You have changed something in your Core Data model, now let's update the UI (the UITableView
). The general pattern you should follow is:
NSManagedObjects
NSManagedObjectContext
NSFetchedResultsControllerDelegate
: update the UITableView
to reflect the changes (add, reload, delete table view rows)So you probably want to do something like that:
//in the method in which you add a new Layer object
- (void)addLayer {
Layer *layer = [NSEntityDescription insertNewObjectForEntityForName:@"Layer" inManagedObjectContext:context];
//recalculate the position of all the other layers
//save NSManagedObjectContext
}
The key is to update the position
of each layer immediately after adding new layer, and then save the context. Then NSFetchedResultsController
will take care of "binding" the layer's position to the row in the table, as you wanted (I assume that you use the position
property in a sort descriptor used in a fetch request). If you implement these NSFetchedResultsControllerDelegate
methods as proposed in documentation, it should do the job:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
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:[tableView cellForRowAtIndexPath:indexPath]
atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
Upvotes: 2