You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 00:39:03