【问题标题】:How to move UITableView cells while using UITableViewDiffableDataSource?使用 UITableViewDiffableDataSource 时如何移动 UITableView 单元格?
【发布时间】:2019-08-15 13:45:19
【问题描述】:

我正在尝试使我的 tableView cells 可移动,但它需要来自 UITableViewDataSource 协议的 2 或 3 个函数,如果我尝试在我的 viewController 中实现委托,它将要求 numberOfRowsInSectioncellForRowAtIndexPath新的UITableViewDiffableDataSource 已经涵盖的功能。

如何在使用新的UITableViewDiffableDataSource 时实现此行为?

【问题讨论】:

  • 不幸的是 moveRow 和 canMove 是 UITableViewDataSource 方法...你有关于这个功能的消息吗?

标签: ios swift uitableview uikit


【解决方案1】:

我可以通过像这样子类化 UITableViewDiffableDataSource 类来做到这一点:

class MyDataSource: UITableViewDiffableDataSource<Int, Int> {

    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        // your code to update source of truth
        // make a new snapshot from your source of truth
        // apply(snapshot, animatingDifferences: false)
    }
}

然后您可以override 实现您需要的任何数据源方法。

【讨论】:

    【解决方案2】:

    为了充实Tung Fam 的答案,这里有一个完整的实现:

    class MovableTableViewDataSource: UITableViewDiffableDataSource<Int, Int> {
    
        override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
            super.tableView(tableView, moveRowAt: sourceIndexPath, to: destinationIndexPath)
    
            var snapshot = self.snapshot()
            if let sourceId = itemIdentifier(for: sourceIndexPath) {
                if let destinationId = itemIdentifier(for: destinationIndexPath) {
                    guard sourceId != destinationId else {
                        return // destination is same as source, no move.
                    }
                    // valid source and destination
                    if sourceIndexPath.row > destinationIndexPath.row {
                        snapshot.moveItem(sourceId, beforeItem: destinationId)
                    } else {
                        snapshot.moveItem(sourceId, afterItem: destinationId)
                    }
                } else {
                    // no valid destination, eg. moving to the last row of a section
                    snapshot.deleteItems([sourceId])
                    snapshot.appendItems([sourceId], toSection: snapshot.sectionIdentifiers[destinationIndexPath.section])
                }
            }
    
            apply(snapshot, animatingDifferences: false, completion: nil)
        }
    }
    

    如果 animatingDifferences 设置为 true,它会崩溃(无论如何,动画在这里并不理想)。

    我不确定是否需要调用super.tableView(move…),但它似乎没有任何害处。

    【讨论】:

    • 这个答案似乎有问题:“if sourceIndexPath.row > destinationIndexPath.row”假设源和目标都在同一个部分,这可能不是真的。每个部分都有自己的 0...n 行编号。
    • 注意:编辑了这个(优秀的)答案,包括在决定快照的位置时注意源/目标部分的额外逻辑。moveItem()。
    • 如果它适用于任何人,我还要指出:如果您将“真实来源”模型数据保留在 TableView 外部,只需更改表格视图中的快照即可 也更新您的模型数据。那是你必须自己做的另一件事。
    • 有趣,这不适用于 iOS 14 (Xcode 12.2)。还需要什么其他设置吗?比如tableView.isEditing = true?
    • @WildCat,是的,UITableView 需要处于编辑模式才能重新排序单元格,isEditing = true。您可能还需要将单元格的编辑模式设置为.none,因为我记得这是在tableView:cellForRowAtIndexPath: 委托方法中完成的。
    最近更新 更多