Ответ 1
Кажется, что вы удаляете indexPath из таблицы, но источник данных таблицы не обновляется. Проверяли ли вы процесс инициализации источника данных NSFetchedResultsController корректно обновлял источник данных таблицы??
У меня есть довольно vanilla 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];
}
Большое спасибо.
Кажется, что вы удаляете indexPath из таблицы, но источник данных таблицы не обновляется. Проверяли ли вы процесс инициализации источника данных NSFetchedResultsController корректно обновлял источник данных таблицы??
Мне бы это понравилось, так как вы заполняете таблицу непосредственно из управляемого контекста, почему бы не удалить ее, сначала удалите объект из управляемого контекста, а затем немедленно обновите таблицу из контекста, используя 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];
}
Я использую CoreData в представлениях таблиц и позволяю пользователям удалять записи и обновлять их все время, я имею их в 4 приложениях, не сталкиваясь с проблемой, о которой вы упомянули.
У меня есть метод FetchedResultsController Delegate в моем коде
- (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];
}