Удалить строку в виде таблицы с помощью fetchedResultController

При удалении swype (большинство импортируемых строк этого метода):

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
 if (editingStyle == UITableViewCellEditingStyleDelete)
  {
    Table *deleteRow = [self.fetchedResultsController objectAtIndexPath:indexPath];
    [self.managedObjectContext deleteObject:deleteRow];
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
  }   
}

При удалении строки я получаю эту ошибку:

Завершение приложения из-за неотображенного исключения "NSInternalInconsistencyException", причина: "Неверное обновление: неверное количество строк в разделе 2.
Количество строк, содержащихся в существующем разделе после обновления (1), должно быть равно количеству строк, содержащихся в этом разделе, перед обновлением (1) плюс или минус количество строк, вставленных или удаленных из этого раздела (вставлено 0, 1 удалено) и плюс или минус количество строк, перемещенных в или из этой секции (0 перемещено в, 0 выведено).

Если я прокомментирую последнюю строку кода ([tableView deleteRowsAtIndexPaths:...]), все работает нормально (но мне нужно обновить представление, чтобы увидеть, что эта строка была удалена).

Как это сделать правильно..?

EDIT: Учитывая @Kyr Dunenkoff responce, я добавил:

- (void)controller:(NSFetchedResultsController*)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath
{
    UITableView *tableV = [self tableView];
    switch(type) {
        case NSFetchedResultsChangeDelete:
            [tableV deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

- (void)controllerDidChangeContent:(NSFetchedResultsController*)controller
{
    [[self tableView] endUpdates];
}

- (void)controllerWillChangeContent:(NSFetchedResultsController*)controller
{
    [[self tableView] beginUpdates];
}

Однако это не изменило ошибку и ошибку. Atm это просто вызвало то, что добавление новых строк больше не работает.

Ответ 1

Чтобы сделать это правильно, реализуйте метод controller:didChangeObject:, внутри него создайте switch для разных типов изменений, в случае NSFetchedResultsChangeDelete введите [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];. Потому что, когда вы работаете с источником данных, например NSFetchedResultsController, все изменения должны поступать оттуда, и ваша таблица только отражает их. Кроме того, вы можете реализовать controller:willChangeContent и controller:didChangeContent, поместить [tableView beginUpdates] в willChange и [tableView endUpdates] в didChange.

Ответ 2

Проблема заключается в том, что, как вы обнаружили, комментируя ее, ваша последняя строка:

[tableView deleteRowsAtIndexPaths:...

В Руководстве по программированию в виде таблицы Apple вам нужно сделать это, но на самом деле вы не должны использовать NSFetchedResultsController. Идея NSFetchedResultsController заключается в том, что он заботится об обновлении пользовательского интерфейса, чтобы синхронизировать его с моделью, чтобы вам не нужно было. Следовательно, если вы просто удаляете объект из модели, получаемый контролер результатов позаботится об остальном.

[Обновить] Упс, не совсем правильно, что я сказал там. Контроллер получаемых результатов не просто "позаботится об остальном" без посторонней помощи. Делегат позаботится об остальном, но кто-то должен реализовать его делегат. Я привык включать вспомогательный класс CoreDataTablewViewController из курса Standford CS193p, который является просто подклассом UITableViewController, который реализует все методы, рекомендованные в документации NSFetchedResultsController. Самое главное, что он реализует методы делегата, которые обновляют представление таблицы при изменении модели.

Итак, ваш обновленный подход все еще верен: вы не должны помещать deleteRowsAtIndexPaths.. в код, который заставляет пользователя удалять их, но пусть выбранный контроллер результатов управляет процессом и реализует делегат, чтобы на самом деле это сделать.

(получите здесь вспомогательный класс: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2011-fall и посмотрите лекцию 14, чтобы узнать, как ее использовать)

Ответ 3

Ответ на

Kyr правильный, но вот дискретный пример удаления NSManagedObject из вашей базы данных, а также из NSFetchedResultsController:

- (void) deleteObjectFromDBAndTable:(NSIndexPath *)indexPath forTable:(UITableView*)tableView
{
    NSLog(@"Deleting object at row %d", indexPath.row);

    // Delete the object from the data source
    NSManagedObject *objToDelete = (NSManagedObject*)[self.fetchedResultsController objectAtIndexPath:indexPath];

    // Delete the object from the database
    [DBHandler deleteObject:objToDelete andSave:YES];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type)
    {
        case NSFetchedResultsChangeDelete:

            [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

            break;
        default:
            break;
    }
}

Ответ 4

Вам нужно убедиться, что вы не вызываете [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; самостоятельно. Удаление объекта из контекста вызовет NSFetchedResultsController, чтобы обновить себя и таблицу.