【问题标题】:iOS8 Swift: deleteRowsAtIndexPaths crashesiOS 8 Swiftui:在 IndexPath 处删除行崩溃
【发布时间】:2014-08-31 03:49:56
【问题描述】:

我在 Swift、iOS 8、Xcode 6 Beta 6 中从我的 tableView 中删除一行时遇到了一些问题。每次我尝试删除一行时,都会遇到类似

的错误

-[UITableView _endCellAnimationsWithContext:] 中的断言失败, /SourceCache/UIKit_Sim/UIKit-3302.3.1/UITableView.m:1581 2014-08-30 20:31:00.971 类目录 [13290:3241692] *** 终止应用程序由于 未捕获的异常“NSInternalInconsistencyException”,原因: '无效更新:第 1 节中的无效行数。 更新 (25) 后包含在现有节中的行必须是 等于该节之前包含的行数 update (25),加上或减去插入或删除的行数 该部分(0 插入,1 删除)和加或减的数量 移入或移出该部分的行(0 移入,0 移出)。

我已经在这里阅读了这个常见问题的所有答案,并且觉得我已经满足了推荐的条件。项目似乎从数据模型中删除 - - 当我重新加载应用程序时,已删除的项目从表中消失了 - 但在适当的 sqlite 文件中似乎有一些残留物,当然数学并没有加起来。吐出 indexPath 的 println 显示了正确的 Section 和 Row。我很困惑。这应该很简单,但我确信我错过了一些愚蠢的东西,我怀疑在数据模型删除中。 Github 上的完整项目。

func numberOfSectionsInTableView(tableView: UITableView!) -> Int {

    return fetchedResultController.sections.count

}


func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
    return fetchedResultController.sections[section].numberOfObjects

    }

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    let cell = tableViewMain.dequeueReusableCellWithIdentifier("CellMain", forIndexPath: indexPath) as UITableViewCell

        let personForRow = fetchedResultController.objectAtIndexPath(indexPath) as Person
        cell.textLabel.text = personForRow.fullName()

        return cell

}

func tableView(tableView: UITableView!, canEditRowAtIndexPath indexPath: NSIndexPath!) -> Bool {
    return true
}

func tableView(tableView: UITableView!, editingStyleForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCellEditingStyle {
    return UITableViewCellEditingStyle.Delete
}

 func tableView(tableView: UITableView!, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath!) {
    println("section and row \(indexPath.section) \(indexPath.row) ")
    if (editingStyle == UITableViewCellEditingStyle.Delete) {
    let personForRow : NSManagedObject = fetchedResultController.objectAtIndexPath(indexPath) as Person
    context?.deleteObject(personForRow)
    context?.save(nil)
        tableViewMain.beginUpdates()
    tableViewMain.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
        tableViewMain.endUpdates()
    }

【问题讨论】:

标签: ios xcode uitableview swift ios8


【解决方案1】:

使用 Xcode Core Data Master-Detail 模板项目可以轻松重现崩溃。作为一般规则,当您使用NSFetchedResultsController 时,您应该真正使用NSFetchedResultsControllerDelegate(您已声明它但不要使用它)。

在您的 tableView:commitEditingStyle:forRowAtIndexPath: 方法中删除这些行:

tableViewMain.beginUpdates()
tableViewMain!.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Fade)
tableViewMain.endUpdates()

并将这些行添加到您的 viewController 类中:

func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableViewMain.beginUpdates()
}

func controller(controller: NSFetchedResultsController!, didChangeSection sectionInfo: NSFetchedResultsSectionInfo!, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
    switch type {
    case .Insert:
        tableViewMain.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
    case .Delete:
        tableViewMain.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: .Fade)
    default:
        return
    }
}

func controller(controller: NSFetchedResultsController!, didChangeObject anObject: AnyObject!, atIndexPath indexPath: NSIndexPath!, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath!) {
    switch type {
    case .Insert:
        tableViewMain.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
    case .Delete:
        tableViewMain.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    case .Update:
        return
        //Should also manage this case!!!
        //self.configureCell(tableView.cellForRowAtIndexPath(indexPath), atIndexPath: indexPath)
    case .Move:
        tableViewMain.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
        tableViewMain.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
    default:
        return
    }
}

func controllerDidChangeContent(controller: NSFetchedResultsController!) {
    tableViewMain.endUpdates()
}

这应该可以解决您的问题。

【讨论】:

  • 感谢您的详细回复。 Xcode 给我这个代码的错误。通过同时拥有 func controller(controller: NSFetchedResultsController!, didChangeSection 和 func controller(controller: NSFetchedResultsController!, didChangeObject 它在第二个控制器函数上给出错误“定义与先前值冲突”。如果我注释掉第一个,错误就会消失。有什么想法吗?
  • 呃,太晚了,我将代码粘贴到了 commitEditingStyle 函数的中间,而不是 viewController 类的根目录。那工作得很好。非常感谢。
  • 这不完全是我的问题。我的代码的问题是 tableView 行数函数在删除行后没有更新其计数。这篇文章暗示我要看看我的那段代码。谢谢!
【解决方案2】:

我相信这只是一个缓存问题。您的fetchedResultController 不会自动重新获取您的结果,因为它会缓存其结果。这意味着当再次调用 tableView:numberOfRowsInSection: 时,即使您删除了一个项目,结果计数仍会返回 25。

【讨论】:

  • @NateBirkholz 它在deleteRowsAtIndexPaths 内部调用tableView:numberOfRowsInSection。这就是它首先打印出错误消息的方式,但更重要的是,这就是它如何验证您尝试添加/删除的行是否真正从数据中删除。
  • 是的,这就是它正在死去的地方......关于决议细节的任何想法? (我最初的反应是不正确的,我添加了更多的调试日志,你是对的,对我的错误感到抱歉。)
  • @NateBirkholz,可能有办法强制fetchedResultsController 丢弃其缓存。但是,如果是我,我会将结果存储在一个临时数组成员中,并将其用于数据源。这样,您可以自己管理数组(删除元素),而不必担心fetchedResults 的实现细节
  • 它曾经加载到一个数组中,但我遇到了很多问题,我的导师建议直接对数据模型进行操作。我将用这种见解进一步挖掘。谢谢。
  • @NateBirkholz,我会推荐额外的抽象,这将使您在必要时更容易将您的应用程序更改为使用不同的数据源。也许您将来会想要添加 Dropbox 支持或其他一些同步服务。在你的控制器和你实际存储信息的方式之间有一个抽象层通常是个好主意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-21
  • 2021-06-13
相关资源
最近更新 更多