Удалить строку в табличном представлении с fetchedResultController - PullRequest
12 голосов
/ 23 января 2012

Во время удаления 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, я добавил:

- (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];
}

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

Ответы [ 5 ]

14 голосов
/ 23 января 2012

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

8 голосов
/ 30 сентября 2012

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

[tableView deleteRowsAtIndexPaths:...

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

[Обновить] Упс, не совсем правильно то, что я сказал только что.Полученный контроллер результатов не просто «позаботится об остальном» без посторонней помощи.Это делегат позаботится обо всем остальном, но кто-то должен реализовать его делегат.У меня есть привычка включать вспомогательный класс CoreDataTablewViewController из курса Standford CS193p, который является просто подклассом UITableViewController, который реализует все методы, рекомендованные в документации NSFetchedResultsController.Наиболее важно то, что он реализует методы делегата, которые обновляют табличное представление при изменении модели.

Таким образом, ваш обновленный подход по-прежнему верен: вы НЕ должны вставлять deleteRowsAtIndexPaths .. в код, который заставляет пользователя удалять их, но пусть выбранный контроллер результатов управляет процессом и реализует делегат для его фактического выполнения.

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

2 голосов
/ 15 декабря 2013

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

2 голосов
/ 17 февраля 2013

Ответ Кира правильный, но вот отдельный пример того, как удалить 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;
    }
}
0 голосов
/ 15 ноября 2018

Как сказал Rhubarb, я реализовал следующие делегаты, и проблема решена

//Fetch delegates implementation from web.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2011-fall (CoreDataTableViewController.zip)
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        self.itemsTableView.beginUpdates()
        beganUpdates = true
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
        switch type {
            case .insert:
                self.itemsTableView.insertSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
            case .delete:
                self.itemsTableView.deleteSections(NSIndexSet(index: sectionIndex) as IndexSet, with: .fade)
            default:
                break
        }
}

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

        switch type {

            case .insert:
                self.itemsTableView.insertRows(at: [newIndexPath!], with: .fade)
            case .delete:
                self.itemsTableView.deleteRows(at: [indexPath!], with: .fade)
            case .update:
                self.itemsTableView.reloadRows(at: [indexPath!], with: .fade)
            case .move:
                self.itemsTableView.deleteRows(at: [indexPath!], with: .fade)
                self.itemsTableView.insertRows(at: [newIndexPath!], with: .fade)
            default:
                break
        }
}

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    if beganUpdates {
        self.itemsTableView.endUpdates()
    }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...