使用NSFetchedResultsController(Swift)移动TableView行后回弹至原位问题
故障根本原因
- 你修改的
myFetchRC.fetchedObjects是NSFetchedResultsController返回的只读快照副本,对这个临时可变数组做的插入、删除操作完全不会修改CoreData持久层的实体数据,也不会同步到FRC的数据源中。 - 调用
CoreDataStack.shared.saveContext()时没有任何实际的实体属性变更需要提交,保存操作是无效的。 - NSFetchedResultsController的结果排序完全依赖初始化时传入的
NSSortDescriptor规则,只要没有修改实体对应的排序字段值,每次FRC拉取数据都会返回原始排序的结果,reload后自然回到初始位置。
解决方法
1. 新增CoreData实体排序字段
给对应业务实体增加一个Int类型的排序字段,比如命名为sortIndex,首次插入数据时按顺序给该字段赋值0、1、2……
2. 修改移动行逻辑,更新排序字段值
移动行时根据移动方向更新所有受影响实体的sortIndex值,再保存上下文,参考代码如下:
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { guard let objects = myFetchRC.fetchedObjects as? [你的业务实体类型] else { return } let movedObject = objects[sourceIndexPath.row] let context = CoreDataStack.shared.context if sourceIndexPath.row < destinationIndexPath.row { // 向下移动:中间范围的对象排序值全部减1 for row in sourceIndexPath.row + 1 ... destinationIndexPath.row { objects[row].sortIndex -= 1 } movedObject.sortIndex = Int16(destinationIndexPath.row) } else { // 向上移动:中间范围的对象排序值全部加1 for row in destinationIndexPath.row ..< sourceIndexPath.row { objects[row].sortIndex += 1 } movedObject.sortIndex = Int16(destinationIndexPath.row) } // 保存上下文,变更会同步到FRC try? context.save() // 若未实现FRC的delegate自动刷新逻辑,可在此处手动调用tableView.reloadData() }
3. 校验FRC的排序规则
初始化FRC时,必须将sortIndex作为第一优先级的排序描述符:
let sortDescriptor = NSSortDescriptor(key: "sortIndex", ascending: true) fetchRequest.sortDescriptors = [sortDescriptor] // 后续FRC初始化逻辑
内容的提问来源于stack exchange,提问作者user16717743
相关产品推荐
相关产品推荐

