Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement re-ordering of CoreData records?

Tags:

ios

core-data

I am using CoreData for my iPhone app, but CoreData doesn't provide an automatic way of allowing you to reorder the records. I thought of using another column to store the order info, but using contiguous numbers for ordering index has a problem. if I am dealing with lots of data, reordering a record potentially involves updating a lot of records on the ordering info (it's sorta like changing the order of an array element)

What's the best way to implement an efficient ordering scheme?

like image 715
Boon Avatar asked Jul 03 '09 02:07

Boon


People also ask

How do I save an object in Core Data?

To save an object with Core Data, you can simply create a new instance of the NSManagedObject subclass and save the managed context. In the code above, we've created a new Person instance and saved it locally using Core Data.


2 Answers

FetchedResultsController and its delegate are not meant to be used for user-driven model changes. See the Apple reference doc. Look for User-Driven Updates part. So if you look for some magical, one-line way, there's not such, sadly.

What you need to do is make updates in this method:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {  userDrivenDataModelChange = YES;   ...[UPDATE THE MODEL then SAVE CONTEXT]...   userDrivenDataModelChange = NO; } 

and also prevent the notifications to do anything, as changes are already done by the user:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {  if (userDrivenDataModelChange) return;  ... } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {  if (userDrivenDataModelChange) return;  ... } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {  if (userDrivenDataModelChange) return;  ... } 

I have just implemented this in my to-do app (Quickie) and it works fine.

like image 81
Aleksandar Vacić Avatar answered Sep 30 '22 09:09

Aleksandar Vacić


Here is a quick example showing a way to dump the fetched results into an NSMutableArray which you use to move the cells around. Then you just update an attribute on the entity called orderInTable and then save the managed object context.

This way, you don't have to worry about manually changing indexes and instead you let the NSMutableArray handle that for you.

Create a BOOL that you can use to temporarily bypass the NSFetchedResultsControllerDelegate

@interface PlaylistViewController () {     BOOL changingPlaylistOrder; } @end 

Table view delegate method:

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {     // Refer to https://developer.apple.com/library/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40008228-CH1-SW14      // Bypass the delegates temporarily     changingPlaylistOrder = YES;      // Get a handle to the playlist we're moving     NSMutableArray *sortedPlaylists = [NSMutableArray arrayWithArray:[self.fetchedResultsController fetchedObjects]];      // Get a handle to the call we're moving     Playlist *playlistWeAreMoving = [sortedPlaylists objectAtIndex:sourceIndexPath.row];      // Remove the call from it's current position     [sortedPlaylists removeObjectAtIndex:sourceIndexPath.row];      // Insert it at it's new position     [sortedPlaylists insertObject:playlistWeAreMoving atIndex:destinationIndexPath.row];      // Update the order of them all according to their index in the mutable array     [sortedPlaylists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {         Playlist *zePlaylist = (Playlist *)obj;         zePlaylist.orderInTable = [NSNumber numberWithInt:idx];     }];      // Save the managed object context     [commonContext save];      // Allow the delegates to work now     changingPlaylistOrder = NO; } 

Your delegates would look something like this now:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject        atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type       newIndexPath:(NSIndexPath *)newIndexPath {     if (changingPlaylistOrder) return;      switch(type)     {         case NSFetchedResultsChangeMove:             [self configureCell:(PlaylistCell *)[self.tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];             break;      } }  - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {     if (changingPlaylistOrder) return;      [self.tableView reloadData]; } 
like image 35
iwasrobbed Avatar answered Sep 30 '22 09:09

iwasrobbed