在swype删除期间(此方法的大多数importatnt行):
- (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:...]
),一切正常(但我必须刷新视图以查看该行已被删除)。
如何正确地做到这一点..?
编辑: 考虑到@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];
}
然而,这并没有改变崩溃和错误。它只是导致添加新行不再有效。
答案 0 :(得分:14)
要正确执行此操作,请实施controller:didChangeObject:
方法,在switch
案例中插入NSFetchedResultsChangeDelete
,在[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
内为不同的更改类型设置controller:willChangeContent
。因为当您使用NSFetchedResultsController等数据源时,所有更改都必须来自那里,而您的表只反映它们。
此外,您可以实施controller:didChangeContent
和[tableView beginUpdates]
,将willChange
放入[tableView endUpdates]
,将didChange
放入{{1}}。
答案 1 :(得分:8)
问题是 - 正如你通过评论发现的那样 - 你的最后一行:
[tableView deleteRowsAtIndexPaths:...
Apple Table View编程指南告诉你需要这样做,但实际上你不应该在使用NSFetchedResultsController时。 NSFetchedResultsController的想法是 it 负责更新UI以使其与模型保持同步,这样您就不需要了。 因此,如果您只是从模型中删除对象,则获取的结果控制器将负责其余部分。
[更新]哎呀,不完全正确我刚才所说的。获得结果控制器不会在没有帮助的情况下“照顾其余”。它的委托将负责其余部分,但有人需要实现它的委托。 我习惯于在Standford CS193p课程中包含CoreDataTablewViewController辅助类,它只是一个UITableViewController子类,它实现了NSFetchedResultsController文档中推荐的所有方法。最重要的是,它实现了在模型更改时更新表视图的委托方法。
所以你的更新方法仍然是正确的:你不应该把deleteRowsAtIndexPaths ..放在导致用户删除它们的代码中,而是让抓取的结果控制器驱动进程,并实现委托实际去做。
(在这里获取帮助类:http://www.stanford.edu/class/cs193p/cgi-bin/drupal/downloads-2011-fall,并观看第14讲以了解如何使用它)
答案 2 :(得分:2)
Kyr的答案是正确的,但这里有一个离散的例子,说明如何从数据库以及NSFetchedResultsController中删除NSManagedObject:
- (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;
}
}
答案 3 :(得分:2)
您需要确保不要自己致电[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
。从上下文中删除对象将触发NSFetchedResultsController
刷新自身和表。
答案 4 :(得分:0)
如大黄所言,我实施了以下代表并解决了问题
//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()
}
}
}