如何通过DiffableDataSource更新Section中的页脚而不引起闪烁效果?

问题描述

节可能包含1个页眉,许多内容项和1个页脚。

对于DiffableDataSource,大多数在线示例都使用enum来表示Section。例如

func applySnapshot(_ animatingDifferences: Bool) {
    var snapshot = Snapshot()
    
    snapshot.appendSections([.MainAsEnum])

    snapshot.appendItems(filteredTabInfos,toSection: .MainAsEnum)

    dataSource?.apply(snapshot,animatingDifferences: animatingDifferences)
}

但是,当Section具有动态内容页脚时,我们可能需要使用struct来表示Section。例如

import Foundation

struct TabInfoSection {

    // Do not include content items [TabInfo] as member of Section. If not,any mutable 
    // operation performed on content items,will misguide Diff framework to throw 
    // away entire current Section,and replace it with new Section. This causes 
    // flickering effect.

    var footer: String
}

extension TabInfoSection: Hashable {
}

但是,我们应该如何只更新页脚?

当前提供的方法

DiffableDataSource: Snapshot Doesn't reload Headers & footers并不完全准确

如果我尝试更新页脚

class TabInfoSettingsController: UIViewController {
    …

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

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

        snapshot.appendItems(filteredTabInfos,toSection: section)

        dataSource?.apply(snapshot,animatingDifferences: animatingDifferences)
    }

var footerValue = 100

extension TabInfoSettingsController: TabInfoSettingsItemCellDelegate {
    func crossButtonClick(_ sender: UIButton) {
        let hitPoint = (sender as AnyObject).convert(CGPoint.zero,to: collectionView)
        if let indexPath = collectionView.indexPathForItem(at: hitPoint) {
            // use indexPath to get needed data

            footerValue = footerValue + 1
            tabInfoSection.footer = String(footerValue)
            
            //
            // Perform UI updating.
            //
            applySnapshot(true)
        }
    }
}

我将得到以下闪烁的结果。

enter image description here

之所以闪烁,是因为diff框架抛出了整个旧的Section,并用新的Section替换了它,因为它发现TabInfoSection对象中有更改。

是否有一种好的方法,可以通过DiffableDataSource更新Section中的页脚,而不会引起闪烁效果

p / s整个项目源代码可以在https://github.com/yccheok/ios-tutorial/tree/broken-demo-for-footer-updating中的TabDemo文件夹下找到。

解决方法

您是否考虑过只为页脚创建部分?这样,在闪烁时就不会重新加载,因为从技术上讲,它不在有问题的部分之外?

,

有一个快速修复程序,但是您将失去tableview的动画。在TabInfoSettingsController.swift中,您可以在此函数中强制设置错误动画:

/network/dependencies

您将看不到闪烁的效果,但是将失去标准动画。

,

如果您只想更新collectionview页脚文本,则将其设置为TabInfoSettingsFooterCell的变量。

var tableSection: TabInfoSettingsFooterCell?

数据源

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
            }
            
            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

        //set tableSection value
        self.tableSection = tabInfoSettingsFooterCell

        return tabInfoSettingsFooterCell
    }
    
    return dataSource
}

TabInfoSettingsItemCellDelegate

func crossButtonClick(_ sender: UIButton) {
    let hitPoint = (sender as AnyObject).convert(CGPoint.zero,to: collectionView)
    if let indexPath = collectionView.indexPathForItem(at: hitPoint) {

        footerValue = footerValue + 1
        tabInfoSection.footer = String(footerValue)

        //Update section value
        self.tableSection?.label.text = String(footerValue)
    }
}