使用UITableViewDiffableDataSource时,UITableView行重排失效如何解决?
解决UITableViewDiffableDataSource下表格行重排无响应的问题
当使用UITableViewDiffableDataSource时,UITableView的dataSource被替换为该diffable数据源对象,你在ViewController中实现的tableView(_:canMoveRowAt:)、tableView(_:moveRowAt:to:)等UITableViewDataSource方法不会被触发——因为这些方法的调用优先级属于当前设置的dataSource对象,也就是diffableDataSource,而非ViewController。
以下是两种可行的解决方案:
方法一:利用DiffableDataSource的闭包配置重排逻辑
UITableViewDiffableDataSource提供了canMoveRowAt和moveRowAt闭包属性,直接设置这些闭包即可实现重排逻辑,无需子类化。
修改你的playsDiffableDataSource方法:
func playsDiffableDataSource(_ tableView: UITableView) -> NewsItemDataSource { let diffableDatasource = NewsItemDataSource(tableView: tableView) { [weak self] tableView, indexPath, objectID -> UITableViewCell? in guard let self = self, let newsItem = try? CoreDataManager.shared.moc.existingObject(with: objectID) as? CDNewsItem else { return UITableViewCell() } let cell: NewsItemTableViewCell = tableView.dequeueReusableCell(for: indexPath) cell.configure(with: newsItem) return cell } // 允许所有行重排 diffableDatasource.canMoveRowAt = { _, _ in return true } // 处理重排后的逻辑 diffableDatasource.moveRowAt = { [weak self] source, destination in guard let self = self, let fetchedObjects = self.fetchedResultsController.fetchedObjects, source.row < fetchedObjects.count, destination.row < fetchedObjects.count else { return } // 注意:仅交换数组无法持久化,需更新CoreData的排序字段 let movedItem = fetchedObjects[source.row] let targetItem = fetchedObjects[destination.row] // 假设给CDNewsItem添加了position字段用于排序 let tempPosition = movedItem.position movedItem.position = targetItem.position targetItem.position = tempPosition // 保存上下文触发FRC更新 do { try CoreDataManager.shared.moc.save() } catch { print("排序保存失败:\(error)") } } return diffableDatasource }
配套调整
- 在CoreData模型中给
CDNewsItem添加position属性(Int类型,非可选) - 更新FRC的排序描述符:
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "position", ascending: true)]
方法二:子类化DiffableDataSource重写重排方法
如果需要更复杂的逻辑,可以子类化UITableViewDiffableDataSource并重写相关数据源方法:
// 自定义可重排的数据源子类 class ReorderableNewsItemDataSource: NewsItemDataSource { weak var reorderHandler: NewsItemViewController? override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { return true } override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { reorderHandler?.handleRowReorder(from: sourceIndexPath, to: destinationIndexPath) } } // 在ViewController中添加处理方法 extension NewsItemViewController { func handleRowReorder(from source: IndexPath, to destination: IndexPath) { guard let fetchedObjects = fetchedResultsController.fetchedObjects, source.row < fetchedObjects.count, destination.row < fetchedObjects.count else { return } // 更新CoreData排序字段并保存 let movedItem = fetchedObjects[source.row] let targetItem = fetchedObjects[destination.row] let tempPosition = movedItem.position movedItem.position = targetItem.position targetItem.position = tempPosition do { try CoreDataManager.shared.moc.save() } catch { print("排序保存失败:\(error)") } } } // 修改数据源创建方法 func playsDiffableDataSource(_ tableView: UITableView) -> ReorderableNewsItemDataSource { let diffableDatasource = ReorderableNewsItemDataSource(tableView: tableView) { [weak self] tableView, indexPath, objectID -> UITableViewCell? in // 原cell配置逻辑不变 guard let self = self, let newsItem = try? CoreDataManager.shared.moc.existingObject(with: objectID) as? CDNewsItem else { return UITableViewCell() } let cell: NewsItemTableViewCell = tableView.dequeueReusableCell(for: indexPath) cell.configure(with: newsItem) return cell } diffableDatasource.reorderHandler = self return diffableDatasource }
额外修正点
- 你的代码中存在类名不匹配错误:
extension PlaysListViewController: NSFetchedResultsControllerDelegate需改为extension NewsItemViewController: NSFetchedResultsControllerDelegate,否则FRC代理方法不会触发。 - 确保
tableView.isEditing = true已正确设置,这是显示重排按钮的必要条件。
内容的提问来源于stack exchange,提问作者Dayanithi Natarajan
相关产品推荐
相关产品推荐

