UITableView 行删除崩溃应用
Posted
技术标签:
【中文标题】UITableView 行删除崩溃应用【英文标题】:UITableView row delete crashes app 【发布时间】:2014-04-21 01:30:08 【问题描述】:我有一个基于 Core Data 的表视图,它有一个简单的删除方法。这是可以在无数教程中找到的简单代码,但是,它会使应用程序崩溃并显示以下消息:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (2) must be equal to the number of rows contained in that section before the update (2), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
删除方法如下:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
if (editingStyle == UITableViewCellEditingStyleDelete)
// get garden to delete, delete it through Core Data
Garden *gardenToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(@"Garden name: %@", gardenToDelete.gardenName);
[self.managedObjectContext deleteObject:gardenToDelete];
[self.managedObjectContext save:nil];
[self.tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
我花了几个小时重新排列代码,删除 beginUpdate
和 endUpdate
调用,检查其他解决方案。非常感谢您的帮助。
【问题讨论】:
什么是self.fetchedResultsController和self.managedObjectContext? 视图的核心数据栈对象 【参考方案1】:您只需要从您的NSFetchedResultsController
中删除该对象,然后假设您已实现docs 中描述的委托方法,获取的结果控制器将更新表格视图。您不应该触摸表格视图行,NSFetchedResultsControllerDelegate
会完成所有这些操作。
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
if (editingStyle == UITableViewCellEditingStyleDelete)
// deleting cell from fetched results contoller
Garden *gardenToDelete = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSLog(@"Garden name: %@", gardenToDelete.gardenName);
[self.managedObjectContext deleteObject:gardenToDelete];
[self.managedObjectContext save:nil];
【讨论】:
试过了,该对象已从核心数据中删除,但该行保持不变。如果您点击该行,应用程序将崩溃(因为该行后面没有数据) @Andrew 查看我在回答中发布的文档链接,您需要在self.fetchedResultsController
上设置delegate
,并实现更新的委托方法【参考方案2】:
您是否使用NSFetchedResultsController
的返回值实现- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
?保存后self.fetchedResultsController
会刷新,但要确保tableView返回值的dataSource全部依赖self.fetchedResultsController
。
【讨论】:
【参考方案3】:你是否实现了这些方法:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.tableView beginUpdates];
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
UITableView *tableView = self.tableView;
// my comment: mapIndexPathFromFetchResultsController function, I just use to get indexPath. You can remove it
indexPath = [self mapIndexPathFromFetchResultsController:indexPath];
newIndexPath = [self mapIndexPathFromFetchResultsController:newIndexPath];
switch(type)
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
UITableViewCell *cell;
cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell != nil)
// my comment: just use the same with cellForRowAtIndexPath
// - (void)syncViewConfigureCell:(UITableViewCell *)cell
// cell.textLabel.text = @""; etc.
[self syncViewConfigureCell:cell];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
- (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)controllerDidChangeContent:(NSFetchedResultsController *)controller
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
【讨论】:
mapIndexPathFromFetchResultsController:
方法是什么? syncViewConfigureCell:
方法是什么?以上是关于UITableView 行删除崩溃应用的主要内容,如果未能解决你的问题,请参考以下文章
编辑模式下的 UITableView - 按 Delete 会使我的应用程序崩溃
NSFetchedResultsController 崩溃的 TableView 行删除?