使用fetchedResultsController删除表视图中的行

时间:2012-01-23 16:11:26

标签: ios objective-c tableview nsfetchedresultscontroller

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

然而,这并没有改变崩溃和错误。它只是导致添加新行不再有效。

5 个答案:

答案 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()
    }
    }
}
相关问题