【前提条件】
- あらかじめ、対象のManagedObjectに「position」というプロパティを作っておきます。
- UITableViewの中身はNSFetchedResultsControllerで管理しておきます。
- NSFetchedResultsControllerに渡すFetchRequestに、「position」でソートするようSortDescriptorを設定しておきます。
// Edit the sort key as appropriate. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"position" ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
セルが移動された際、以下のようなコードでpositionの値を書き換えることで、入れ替えが実現できました。
クラス名は、対象にするモデルクラスに書き換えてください。
// Override to support rearranging the table view. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { NSManagedObjectContext *context = [[MRAppDelegate sharedDelegate] managedObjectContext]; MyObject* targetObject = (MyObject *)[self.fetchedResultsController objectAtIndexPath:fromIndexPath]; MyObject* previousObject = nil; MyObject* nextObject = nil; id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:fromIndexPath.section]; if (fromIndexPath.row < toIndexPath.row) { previousObject = (MyObject *)[self.fetchedResultsController objectAtIndexPath:toIndexPath]; if ([sectionInfo numberOfObjects] - 1 != toIndexPath.row) { nextObject = (MyObject *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:toIndexPath.row + 1 inSection:toIndexPath.section]]; } } else { if (toIndexPath.row != 0) { previousObject = (MyObject *)[self.fetchedResultsController objectAtIndexPath:[NSIndexPath indexPathForRow:toIndexPath.row - 1 inSection:toIndexPath.section]]; } nextObject = (MyObject *)[self.fetchedResultsController objectAtIndexPath:toIndexPath]; } float sakura = 1; float aya = 0; if (nextObject) { if (previousObject) { sakura = (nextObject.position - previousObject.position) / 2; } else { aya = nextObject.position - sakura * 2; } } if (previousObject) { aya = previousObject.position; } targetObject.position = sakura + aya; [context save:nil]; }
ただ、これだけだとTableViewに更新が行き渡らないので、
以下のように、controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:
にてフックして、reloadDataをするとうまくいきます。
- (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 reloadData]; break; } }
「position」というプロパティを作らず、ArrangedなRelationshipを定義して、それを使ってソートする方法もありますが、
iOS 4が対応していないため今回は使っていません。