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?
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.
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.
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]; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With