DiffableDataSource-没有动画,但是在删除操作过程中闪烁

时间:2020-10-11 16:26:25

标签: ios swift

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

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

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

效果很好

enter image description here

完整的源代码(带有可行的删除动画的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)

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

enter image description here

完整的源代码(在删除过程中具有闪烁效果的TabDemo)

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


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

1 个答案:

答案 0 :(得分:0)

当前,我们不是使用枚举,而是使用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?