这是我对委托的实现:
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.beginUpdates()
ItemListTableViewController.logger.log("begin updates")
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
tableView.endUpdates()
ItemListTableViewController.logger.log("end updates")
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
ItemListTableViewController.logger.log(type.rawValue, "\(indexPath) -> \(newIndexPath)")
switch type {
case .delete:
self.tableView.deleteRows(at: [indexPath!], with: .automatic)
case .insert:
self.tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .move:
self.tableView.moveRow(at: indexPath!, to: newIndexPath!)
case .update:
self.tableView.reloadRows(at: [indexPath!], with: .automatic)
}
}
这是我得到的日志:
ItemListTableViewController >>> "perform insert start"
ItemListTableViewController >>> "begin updates"
ItemListTableViewController >>> 1 "nil -> Optional([0, 0])"
ItemListTableViewController >>> 4 "Optional([0, 1]) -> Optional([0, 2])"
ItemListTableViewController >>> "end updates"
ItemListTableViewController >>> "perform insert end"
我尝试通过调用context.insert(item)
将新项目插入上下文,并根据第3行日志插入新项目。并且控制器根据第4行移动一些项目。但原始价值的类型&#39; 4&#39; NSFetchedResultsChangeType
中的update
应该是move
,而不是move
。
我还测试了其他情况,当我需要更新项目时,它给了我一个 update
类型。
我错了move
和{{1}}的含义吗?或者它是一个错误?
我错了。这是一个糟糕的代码。
我正在使用Xcode 8.3.1
答案 0 :(得分:0)
4是update
的枚举值; 3是move
的值。您可能会被委派的两个索引路径混淆。 indexPath
是更新前的索引(在任何插入或删除之前),newIndexPath
是更新后的索引。不幸的是,当涉及到如何使用fetchedResults控制器进行更新时,Apple的文档是错误的。对于更新,您应该使用newIndexPath
,因为在插入和删除后处理更新。由于这个原因,您可能会遇到崩溃或不良行为。
此外,您处理移动的方式也不正确。见App crashes after updating CoreData model that is being displayed in a UITableView
答案 1 :(得分:0)
感谢@Joe Rose,我明白这是如何运作的。我不是在处理可能同时插入和移动的任务,所以我没有使用你的解决方案。
我对update
和move
的含义错了。所以我再次阅读Apple的文档。它说:
使用以下启发式方法报告更改:
在添加和删除操作中,仅报告添加/删除的对象。
假设受影响对象之后的所有对象也被移动,但不会报告这些移动。
当对象的已更改属性是获取请求中使用的排序描述符之一时,将报告移动。
在这种情况下,假设更新了对象,但没有向委托发送单独的更新消息。
当对象的状态发生更改时,将报告更新,但更改的属性不是排序键的一部分。
因此,报告move
时,也会报告update
。那时,获取的对象会更新,但tableView不会更新,因此我需要使用indexPath
来定位单元格,并newIndexPath
来获取正确的对象。
这是我的最终解决方案,现在表现良好:
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
ItemListTableViewController.logger.log(type.rawValue, "\(indexPath) -> \(newIndexPath)")
switch type {
case .insert:
self.tableView.insertRows(at: [newIndexPath!], with: .automatic)
case .delete:
self.tableView.deleteRows(at: [indexPath!], with: .automatic)
case .move:
self.tableView.moveRow(at: indexPath!, to: newIndexPath!)
fallthrough
case .update:
self.configure(cell: tableView.cellForRow(at: indexPath!) as! ItemInfoCell, at: newIndexPath!)
}
}
func configure(cell: ItemInfoCell, at indexPath: IndexPath) {
let item = fetchController.object(at: indexPath)
cell.item = item
}
如果有任何问题,请告诉我。