tableView: cellForRowAtIndexPath вызывается с нулевым indexPath после удаления элемента - PullRequest
15 голосов
/ 04 августа 2011

У меня довольно ванильный UITableView, управляемый NSFetchedResultsController для отображения всех экземпляров данного объекта Core Data.

Когда пользователь удаляет запись в табличном представлении, проводя по ней, tableView:cellForRowAtIndexPath: в конечном счете вызывается на мой UITableViewController с nil indexPath.Поскольку я не ожидал, что он будет вызван с nil indexPath, приложение аварийно завершает работу.

Я могу обойти эту проблему, проверив это значение nil и вернув пустую ячейку.Кажется, это работает, но я все еще волнуюсь, что, возможно, я справился с чем-то не так.Есть идеи?Кто-нибудь когда-либо видел, чтобы tableView:cellForRowAtIndexPath: вызывался с nil indexPath?

Обратите внимание, что это происходит только тогда, когда пользователь удаляет из представления таблицы, проводя по ячейке. При удаленииэлемент, использующий режим редактирования табличного представления, этого не происходит.Что может отличаться между двумя способами удаления ячейки?

Итак, действительно ли это нормальная ситуация для получения nil indexPath в методе делегата табличного представления?

Мое представлениекод контроллера действительно стандартный.Вот удаление:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        NSManagedObject *managedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
        [self.moc deleteObject:managedObject];
        NSError *error = NULL;
        Boolean success = [self.moc save:&error];
        if (!success) { <snip> }
        // actual row deletion from table view will be handle from Fetched Result Controller delegate
        // [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else { <snip> }   
}

Это приведет к вызову метода делегата NSFetchedResultsController:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
   atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;
    switch(type) {

        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

        case NSFetchedResultsChangeInsert: <snip> break;
        case NSFetchedResultsChangeUpdate: <snip> break;
        case NSFetchedResultsChangeMove: <snip> break;
    }
}

И, конечно, методы источника данных обрабатываются NSFetchedResultsController Например:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:section];
    return [sectionInfo numberOfObjects];
}

Большое спасибо.

Ответы [ 3 ]

2 голосов
/ 27 июля 2013

Похоже, вы удаляете indexPath из таблицы, но источник данных таблицы не обновляется. Вы проверили, что процесс обновления источника данных NSFetchedResultsController правильно обновляет источник данных таблицы?

0 голосов
/ 25 сентября 2013

Я использую CoreData в табличных представлениях и позволяю пользователям удалять записи и обновлять их все время, у меня они есть в 4 приложениях, не сталкиваясь с упомянутой вами проблемой.

В моем коде есть метод делегирования FetchedResultsController

     - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
  // The fetch controller is about to start sending change notifications, so prepare the table view for updates.
    [self.myTableView beginUpdates];
 }


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    // The fetch controller has sent all current change notifications, so tell the table view to process all updates.
        [self.myTableView endUpdates];

}

0 голосов
/ 17 мая 2013

Я бы так поступил, поскольку вы заполняете таблицу непосредственно из своего управляемого контекста, почему бы при удалении сначала не удалить объект из управляемого контекста, а затем немедленно обновить таблицу из контекста, используя reloadData. Но, используя ваш подход, я думаю, вам нужно добавить beginUpdates и endUpdates:

#pragma mark -
#pragma mark Fetched results controller delegate


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


- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {

switch(type) {
    case NSFetchedResultsChangeInsert:
        [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


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

UITableView *tableViews = self.tableView;

switch(type) {

    case NSFetchedResultsChangeInsert:
        [tableViews insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [tableViews deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeUpdate:
        [_delegate configureCell:[tableViews cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
        break;

    case NSFetchedResultsChangeMove:
        [tableViews deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableViews insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView endUpdates];
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView scrollToRowAtIndexPath:indexPath
                      atScrollPosition:UITableViewScrollPositionMiddle
                              animated:NO];
}
...