UITableView 以级联动画的方式进行行的删除和插入

Posted

技术标签:

【中文标题】UITableView 以级联动画的方式进行行的删除和插入【英文标题】:UITableView performing row deletion and insertion in a cascading animation manner 【发布时间】:2017-04-20 16:58:21 【问题描述】:

我有兴趣使用动画tableView.deleteRows(at: [singleIndexPath], with: .automatic)UITableView 中删除一批行。然而,我的目标是级联删除动画,以便它们以级联动画的方式发生,这样,动画将显示它们一个接一个地被删除,而不是整批被删除。

我发现了一个有用的 UITableView 扩展,在动画完成后我会得到一个完成块,例如:

extension UITableView 

    /// Perform a series of method calls that insert, delete, or select rows and sections of the table view.
    /// This is equivalent to a beginUpdates() / endUpdates() sequence,
    /// with a completion closure when the animation is finished.
    /// Parameter update: the update operation to perform on the tableView.
    /// Parameter completion: the completion closure to be executed when the animation is completed.

    func performUpdate(_ update: ()->Void, completion: (()->Void)?) 

        CATransaction.begin()
        CATransaction.setCompletionBlock(completion)

        // Table View update on row / section
        beginUpdates()
        update()
        endUpdates()

        CATransaction.commit()
    


我的问题是弄清楚如何利用块自动化行删除(以及后来的行插入)的动画。现在,下面两行 IndexPaths 的示例代码仍然同时出现动画,这不是我的目标。

    let singleIndexPath = IndexPath(row: 0, section: 0)
    let anotherIndexPath = IndexPath(row: 1, section: 0)

    self.tableView.performUpdate(

        self.tableView.deleteRows(at: [singleIndexPath, anotherIndexPath], with: .automatic)
        self.tableView.insertRows(at: [singleIndexPath, anotherIndexPath], with: .left)
        //Happening at the same time, which is not what I want
    , completion: 

    )

非常感谢任何提示。

【问题讨论】:

您是否必须将要删除的每个索引路径放入单独调用performUpdate 的更新处理程序中? @paulvs 是的,但这不会自动进行,也不会基于我拥有的行数。我可以对行进行硬编码,但它是无效的。 【参考方案1】:

级联表视图行删除

用法:

let indexPathsToDelete = [IndexPath(row: 1, section: 0), IndexPath(row: 4, section: 0), IndexPath(row: 6, section: 0)]
 deleteRowsAt(indexPaths: indexPathsToDelete, in: tableView)

代码:

func deleteRowsAt(indexPaths: [IndexPath], in tableView: UITableView) 
    var state = (0 ..< tableView.numberOfSections).map 
        Array(0 ..< tableView.numberOfRows(inSection: $0))
    

    func cascadeDeleteAt(indexPath indexPathToDelete: IndexPath) 

        // Get the current row for the index path we wish to delete.
        // It could be diffrent to its original index path if other rows have been deleted.
        guard let currentRow = state[indexPathToDelete.section].index(of: indexPathToDelete.row) else  return 

        let currentIndexPath = IndexPath(row: currentRow, section: indexPathToDelete.section)
        print(currentIndexPath)

        // --- IMPLEMENT THE DELETION FROM YOUR DATA SOURCE ---
        source.remove(at: currentIndexPath.row)


        state[indexPathToDelete.section].remove(at: currentIndexPath.row)

        tableView.performUpdate(
            tableView.deleteRows(at: [currentIndexPath], with: .left)
        , completion: 
            guard var idx = indexPaths.index(of: indexPathToDelete) else  return 
            idx += 1
            if idx < indexPaths.count 
                let nextIndexPathToDelete = indexPaths[idx]
                cascadeDeleteAt(indexPath: nextIndexPathToDelete)
            
        )
    

    if let indexPath = indexPaths.first 
        cascadeDeleteAt(indexPath: indexPath)
    

当以级联方式删除行(即一次删除一行而不是一次删除所有行)时,棘手的部分是:

    删除一行后,其他索引(索引路径以及数据源的索引)不再指向它们原来的位置。 需要从递归函数中调用函数performUpdate,以允许仅在删除当前行后删除下一行。

【讨论】:

这真是太棒了。我试图找出一个递归解决方案,并遇到了你提到的问题,IndexPaths 没有指向他们过去的位置。你设法优雅地解决了这个问题。谢谢!

以上是关于UITableView 以级联动画的方式进行行的删除和插入的主要内容,如果未能解决你的问题,请参考以下文章

获取将在 UITableView 中插入的行的索引

EasyDSS出现重复推流以级直播状态混乱的情况如何处理?

在视图出现之前进行 UITableView 选择

如何检查当前 UITableView 行的文档文件大小

如何启用移动但禁用 UITableView 中某些行的删除

获取 UITableView 中最近添加的行的 indexPath