Здесь полное решение, как управлять индексированной таблицей с основными данными. Ваш атрибут называется displayOrder
, я называю его index
.
Прежде всего, вам лучше разделить вид контроллера и модель. Для этого я использую контроллер модели, который является интерфейсом между представлением и моделью.
Есть 3 случая, когда вам нужно управлять, на которые пользователь может влиять через контроллер представления.
- Добавление нового объекта
- Удаление существующего объекта
- Изменение порядка объектов.
Первые два случая Добавление и удаление довольно просты. Delete вызывает подпрограмму с именем renewObjectIndicesUpwardsFromIndex
для обновления индексов после удаленного объекта.
- (void)createObjectWithTitle:(NSString*)title {
FFObject* object = [FFObject insertIntoContext:self.managedObjectContext];
object.title = title;
object.index = [NSNumber numberWithInteger:[self numberTotalObjects]];
[self saveContext];
}
- (void)deleteObject:(FFObject*)anObject {
NSInteger objectIndex = [anObject.index integerValue];
[anObject deleteObject];
[self renewObjectIndicesUpwardsFromIndex:objectIndex];
[self saveContext];
}
- (void)renewObjectIndicesUpwardsFromIndex:(NSInteger)fromIndex {
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index > %d)", fromIndex];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError* fetchError = nil;
NSArray* objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
NSInteger index = fromIndex;
for (FFObject* object in objects) {
object.index = [NSNumber numberWithInteger:index];
index += 1;
}
[self saveContext];
}
Прежде чем перейти к процедурам контроллера для повторного заказа, здесь часть в контроллере вида. Я использую bool isModifyingOrder
похожий на этот ответ . Обратите внимание, что контроллер вида вызывает две функции в контроллере moveObjectOrderUp
и moveObjectOrderDown
. В зависимости от того, как вы отображаете объекты в табличном представлении - сначала самые новые или самые последние - вы можете переключать их.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
isModifyingOrder = YES;
NSUInteger fromIndex = sourceIndexPath.row;
NSUInteger toIndex = destinationIndexPath.row;
if (fromIndex == toIndex) {
return;
}
FFObject *affectedObject = [self.fetchedResultsController.fetchedObjects objectAtIndex:fromIndex];
NSInteger delta;
if (fromIndex < toIndex) {
delta = toIndex - fromIndex;
NSLog(@"Moved down by %lu cells", delta);
[self.objectController moveObjectOrderUp:affectedObject by:delta];
} else {
delta = fromIndex - toIndex;
NSLog(@"Moved up by %lu cells", delta);
[self.objectController moveObjectOrderDown:affectedObject by:delta];
}
isModifyingOrder = NO;
}
А вот и часть в контроллере. Это может быть написано лучше, но для понимания это может быть лучше.
- (void)moveObjectOrderUp:(FFObject*)affectedObject by:(NSInteger)delta {
NSInteger fromIndex = [affectedObject.index integerValue] - delta;
NSInteger toIndex = [affectedObject.index integerValue];
if (fromIndex < 1) {
return;
}
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index >= %d) AND (index < %d)", fromIndex, toIndex];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError* fetchError = nil;
NSArray* objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
for (FFObject* object in objects) {
NSInteger newIndex = [object.index integerValue] + 1;
object.index = [NSNumber numberWithInteger:newIndex];
}
affectedObject.index = [NSNumber numberWithInteger:fromIndex];
[self saveContext];
}
- (void)moveObjectOrderDown:(FFObject*)affectedObject by:(NSInteger)delta {
NSInteger fromIndex = [affectedObject.index integerValue];
NSInteger toIndex = [affectedObject.index integerValue] + delta;
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Object" inManagedObjectContext:self.managedObjectContext]];
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(index > %d) AND (index <= %d)", fromIndex, toIndex];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError* fetchError = nil;
NSArray* objects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&fetchError];
for (FFObject* object in objects)
{
NSInteger newIndex = [object.index integerValue] - 1;
object.index = [NSNumber numberWithInteger:newIndex];
}
affectedObject.index = [NSNumber numberWithInteger:toIndex];
[self saveContext];
}
Не забудьте использовать второй BOOL
в вашем контроллере вида для действия удаления, чтобы уведомление о перемещении ничего не делало. Я называю это isDeleting
и помещаю это здесь.
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
if (isModifyingOrder) return;
...
switch(type) {
...
case NSFetchedResultsChangeMove:
if (isDeleting == false) {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:localIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:localNewIndexPath]withRowAnimation:UITableViewRowAnimationFade];
}
break;
...
}
}