DiffableDataSource - 无动画,但在删除操作期间闪烁

Posted

技术标签:

【中文标题】DiffableDataSource - 无动画,但在删除操作期间闪烁【英文标题】:DiffableDataSource - No animation, but flickering during delete operation 【发布时间】:2020-10-11 16:26:25 【问题描述】:

以前,我有一个非常简单的代码,它对UICollectionView 执行删除操作。

// Remove from single source of truth.
viewController?.deleteTabInfo(indexPath)

//
// Perform UI updating.
//
self.collectionView.deleteItems(at: [indexPath])

效果很好

完整的源代码(带有可行删除动画的TabDemo)

https://github.com/yccheok/ios-tutorial/tree/7a6dafbcc8c61dd525bd82ae10c6a3bd67538b7f


最近,我们计划迁移到DiffableDataSource

我们的模型非常简单

型号

struct TabInfo 
    let id: Int64
    let type: TabInfoType
    var name: String?
    var colorIndex: Int


extension TabInfo: Hashable 


struct TabInfoSection 
    var tabInfos: [TabInfo]
    var footer: String


extension TabInfoSection: Hashable 

数据来源

func makeDataSource() -> DataSource 
    let dataSource = DataSource(
        collectionView: collectionView,
        cellProvider:  (collectionView, indexPath, tabInfo) -> UICollectionViewCell? in
            guard let tabInfoSettingsItemCell = collectionView.dequeueReusableCell(
                withReuseIdentifier: TabInfoSettingsController.tabInfoSettingsItemCellClassName,
                for: indexPath) as? TabInfoSettingsItemCell else 
                return nil
            
            
            // This is used to handle delete button click event.
            tabInfoSettingsItemCell.delegate = self
            tabInfoSettingsItemCell.reorderDelegate = self

            tabInfoSettingsItemCell.textField.text = tabInfo.getPageTitle()
            
            return tabInfoSettingsItemCell
        
    )
    
    dataSource.supplementaryViewProvider =  collectionView, kind, indexPath in
        guard kind == UICollectionView.elementKindSectionFooter else 
            return nil
        
    
        let section = dataSource.snapshot().sectionIdentifiers[indexPath.section]
        
        guard let tabInfoSettingsFooterCell = collectionView.dequeueReusableSupplementaryView(
            ofKind: kind,
            withReuseIdentifier: TabInfoSettingsController.tabInfoSettingsFooterCellClassName,
            for: indexPath) as? TabInfoSettingsFooterCell else 
            
            return nil
        
        
        tabInfoSettingsFooterCell.label.text = section.footer
        
        return tabInfoSettingsFooterCell
    
    
    return dataSource

快照

var filteredSection: TabInfoSection 
    guard let viewController = self.viewController else 
        return TabInfoSection(tabInfos: [], footer: "")
    
    
    return TabInfoSection(
        tabInfos: viewController.tabInfos.filter( $0.type != TabInfoType.Settings ),
        footer: "This is footer"
    )


func applySnapshot(_ animatingDifferences: Bool) 
    var snapshot = Snapshot()

    let section = filteredSection;
    
    snapshot.appendSections([section])

    snapshot.appendItems(section.tabInfos, toSection: section)

    dataSource?.apply(snapshot, animatingDifferences: animatingDifferences)

删除操作

// Remove from single source of truth.
viewController?.deleteTabInfo(indexPath)

//
// Perform UI updating.
//
applySnapshot(true)

但是,结果并不乐观。不是删除动画,而是显示闪烁效果。

完整的源代码(删除时有闪烁效果的TabDemo)

https://github.com/yccheok/ios-tutorial/tree/c26ce159472fe2d25d181f9835ef11f1081b0bbc


您知道我们遗漏了哪些步骤,导致删除动画无法正常工作吗?

【问题讨论】:

【参考方案1】:

目前,我们不使用 enum,而是使用 struct 来表示 Section。

原因是,我们有一个动态的内容页脚。使用 struct 可以为页脚携带动态内容信息。

我们最初的 Section 类如下所示

import Foundation

struct TabInfoSection 
    var tabInfos: [TabInfo]
    var footer: String


extension TabInfoSection: Hashable 

但是,这是一个错误。因为,我们将内容项 TabInfo 包含为 Section 的成员。

当对内容项执行任何可变操作时,这会导致 Diff 框架丢弃整个当前 Section,并用新 Section 替换它。 (因为 Diff 框架检测到 Section 有变化)。

这会导致闪烁效果。

正确的实现应该是

import Foundation

struct TabInfoSection 
    var footer: String


extension TabInfoSection: Hashable 

P/s 但是,当我们想要显式更新页脚时,这会导致额外的问题。我在另一个问题中描述它 - How to update footer in Section via DiffableDataSource without causing flickering effect?

【讨论】:

以上是关于DiffableDataSource - 无动画,但在删除操作期间闪烁的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewController + diffableDataSource - 当您的手指仍在其上时,单元格不会突出显示

DiffableDataSource CollectionView 在部分中不返回任何项目

DiffableDataSource 抛出“致命:提供的标识符不是唯一的。”将 MPMediaItem 包装在结构中时

DiffableDataSource引发“致命:提供的标识符不是唯一的。”将MPMediaItem包装在结构中时

UITableView diffable 数据源

带有 jQ​​uery 动画的慢速/无响应动画