NSFetchedResultsControllerDelegate 返回错误的 NSFetchedResultsChangeType
NSFetchedResultsControllerDelegate returning wrong NSFetchedResultsChangeType
这是我对委托的实现:
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 行移动一些项目。但是 NSFetchedResultsChangeType
中原始值 '4' 的类型应该是 update
而不是 move
.
我也测试过其他情况,当我需要更新一个项目时,它给了我一个move
类型。
我对 update
和 move
的含义有误吗?或者这是一个错误?
我错了。这是一个错误的代码。
我正在使用 Xcode 8.3.1
4 是 update
的枚举值; 3 是 move
的值。您可能会对被委托的两个索引路径感到困惑。 indexPath
是更新前的索引(在任何插入或删除之前),newIndexPath
是更新后的索引。不幸的是,关于如何使用 fetchedResults 控制器进行更新,Apple 的文档是错误的。对于更新,您应该使用 newIndexPath
因为更新是在插入和删除之后处理的。因此,您可能会遇到崩溃或不良行为。
另外你处理move的方式也不对。参见
感谢@Joe Rose,我明白这是如何工作的。我不处理可能同时插入和移动的任务,所以我没有使用你的解决方案。
我错了 update
和 move
的意思。所以我再次阅读了苹果的文档。它说:
Changes are reported with the following heuristics:
On add and remove operations, only the added/removed object is reported.
It’s assumed that all objects that come after the affected object are also moved, but these moves are not reported.
A move is reported when the changed attribute on the object is one of the sort descriptors used in the fetch request.
An update of the object is assumed in this case, but no separate update message is sent to the delegate.
An update is reported when an object’s state changes, but the changed attributes aren’t part of the sort keys.
所以报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
}
如果有什么问题,请告诉我。
这是我对委托的实现:
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 行移动一些项目。但是 NSFetchedResultsChangeType
中原始值 '4' 的类型应该是 update
而不是 move
.
我也测试过其他情况,当我需要更新一个项目时,它给了我一个move
类型。
我对 update
和 move
的含义有误吗?或者这是一个错误?
我错了。这是一个错误的代码。
我正在使用 Xcode 8.3.1
4 是 update
的枚举值; 3 是 move
的值。您可能会对被委托的两个索引路径感到困惑。 indexPath
是更新前的索引(在任何插入或删除之前),newIndexPath
是更新后的索引。不幸的是,关于如何使用 fetchedResults 控制器进行更新,Apple 的文档是错误的。对于更新,您应该使用 newIndexPath
因为更新是在插入和删除之后处理的。因此,您可能会遇到崩溃或不良行为。
另外你处理move的方式也不对。参见
感谢@Joe Rose,我明白这是如何工作的。我不处理可能同时插入和移动的任务,所以我没有使用你的解决方案。
我错了 update
和 move
的意思。所以我再次阅读了苹果的文档。它说:
Changes are reported with the following heuristics:
On add and remove operations, only the added/removed object is reported.
It’s assumed that all objects that come after the affected object are also moved, but these moves are not reported.
A move is reported when the changed attribute on the object is one of the sort descriptors used in the fetch request.
An update of the object is assumed in this case, but no separate update message is sent to the delegate.
An update is reported when an object’s state changes, but the changed attributes aren’t part of the sort keys.
所以报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
}
如果有什么问题,请告诉我。