NSFetchedResultsController - 用户驱动的更改

Posted

技术标签:

【中文标题】NSFetchedResultsController - 用户驱动的更改【英文标题】:NSFetchedResultsController - User Driven Changes 【发布时间】:2018-03-02 13:49:41 【问题描述】:

概述

我有一个NSFetchedResultsController 用户将能够添加新记录(编辑模式下的表格视图) 当用户点击添加按钮时,我能够检测到事件并创建一个新的 CarNSManagedObject 的子类,与 NSFetchedResultsController 的谓词匹配)

问题:

当用户启动操作时,如何在表格视图中插入新行? 根据我当前的实现,应用程序崩溃。崩溃消息如下。 如何准确检测模型更改何时生效? (根据崩溃消息,我觉得我插入行太早了)

注意:

我知道NSFetchedResultsControllerDelegate 检测到模型更改,但问题是模型已更新,我需要表格视图来匹配它。 通常NSFetchedResultsControllerDelegate 会检测模型更改,我可以使用委托方法进行更新。 我的问题是,由于用户添加了行,模型首先更新,然后表格视图必须根据它进行调整。

参考:https://developer.apple.com/documentation/coredata/nsfetchedresultscontrollerdelegate

NSFetchedResultsController 的创建:

let fetchRequest : NSFetchRequest<Car> = Car.fetchRequest()

fetchRequest.predicate = NSPredicate(format: "color = %@", argumentArray: ["green"])

let orderIDSortDescriptor = NSSortDescriptor(keyPath: \Car.price, ascending: true)

fetchRequest.sortDescriptors = [orderIDSortDescriptor]

fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest,
                                                      managedObjectContext: context,
                                                      sectionNameKeyPath: nil,
                                                      cacheName: nil)

编辑风格

override func tableView(_ tableView: UITableView,
                        editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle 

    let newCarIndex = fetchedResultsController?.fetchedObjects?.count ?? 0

    let editingStyle : UITableViewCellEditingStyle

    switch indexPath.row 

    case newCarIndex:
        editingStyle = .insert

    default:
        break            
    

    return editingStyle

提交用户操作

override func tableView(_ tableView: UITableView,
                        commit editingStyle: UITableViewCellEditingStyle,
                        forRowAt indexPath: IndexPath) 

    switch editingStyle 

    case .insert:
        createGreenCar(at: indexPath) //Creating a new Car with color = Green
        tableView.insertRows(at: [indexPath], with: .automatic) //This causes the app to crash
    default:
        break
    

崩溃错误信息:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

【问题讨论】:

几个问题:你实现了 FRC 委托方法吗?你设置了FRC的代表吗? createGreenCar(at:) 是做什么的?谢谢。 你的错误是invalid number of rows in section 1 请你发布你的tableView numberOfRowsInSection 代码吗? @Jake 它是一个包含多个部分的大型项目的一部分,此处粘贴的代码是用于测试的简化版本。这个问题也出现在 1 个部分。我会更新错误 @pbasdf FRC 委托未设置。 createGreenCar 创建了一辆绿色汽车,以便 FRC 可以取走它。我觉得我在它反映在 FRC 之前将行插入到表视图中 移动是这样,但插入和删除是这样。 【参考方案1】:

感谢@Jake 和@pbasdf,他们的建议帮助我发现并纠正了问题。

我正在回答完整性。

根本原因:

我的表格视图中有多个部分,我将行插入错误的部分。因此,当模型发生变化时,相关部分中的表格视图行数并没有发生变化。

接近用户驱动的更新:

1。使用数组

我觉得将结果转换成数组并使用数组作为数据源而不是NSFetchedResultsController 用于用户驱动的更新会更好。

2。使用 NSFetchedResultsController:

当用户插入/删除/移动行时UITableViewDataSource方法被调用:

当用户插入/删除行时tableView(_:commit:forRowAt:)将被调用 当用户移动行时,tableView(_:moveRowAt:to:) 将被调用 为上述方法相应地更新核心数据

更新核心数据会导致NSFetchedResultsControllerDelegate被调用

controller(_:didChange:at:for:newIndexPath:) 中执行以下操作: 对于插入 - 添加 indexPaths 对于删除 - 删除 indexPaths 对于移动 - 什么都不做,因为 UI 已经是最新的(用户已移动行),稍后在 controllerDidChangeContent(_:) 延迟 0.5 秒后调用 tableView.reloadData()

注意:

当用户移动ios 11.2 上的行(使用NSFetchedResultsController)时,我确实遇到了以下警告:

UITableView internal inconsistency: _visibleRows and _visibleCells must be of same length. _visibleRows

我不知道如何解决它,所以暂时坚持使用数组实现。

【讨论】:

以上是关于NSFetchedResultsController - 用户驱动的更改的主要内容,如果未能解决你的问题,请参考以下文章

在 Core Data 应用程序中调用 performFetch 后,是不是需要手动更新表视图?