1

我想创建一个collectionView也可以折叠的动态部分。使用 iOS 14中的新功能,这似乎很容易section snapshots。这就是我所拥有的(完全工作的示例)。

import UIKit

enum Section: Hashable {
    case group(Int)
}

enum Item: Hashable {
    case header(Int)
    case item(String)
}

class ViewController: UIViewController {

    private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!

    private var groups = [1, 2, 3, 4, 5]
    private var groupItems: [Int: [String]] = [
        1: ["A", "B", "C", "D"],
        2: ["E", "F", "G", "H"],
        3: ["I", "J", "K", "L"],
        4: ["M", "N", "O", "P"],
        5: ["Q", "R", "S", "T"],
    ]
    
    private lazy var collectionView: UICollectionView = {
        var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        config.headerMode = .firstItemInSection
        
        let layout = UICollectionViewCompositionalLayout.list(using: config)
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        
        collectionView.backgroundColor = .systemGroupedBackground
        
        return collectionView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(collectionView)
        collectionView.frame = view.bounds
        
        configureDataSource()
        applySnapshot()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
            self.groups.remove(at: 0) // This removes the entire first section
            self.applySnapshot()
        }
    }
    
    private func configureDataSource() {
        
        let itemCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> {
            (cell, indexPath, letter) in
            
            var content = cell.defaultContentConfiguration()
            content.text = letter
            cell.contentConfiguration = content

        }
        
        let headerCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> {
            (cell, indexPath, number) in
            
            var content = cell.defaultContentConfiguration()
            
            content.text = String(number)
            cell.contentConfiguration = content
            
            let headerDisclosureOption = UICellAccessory.OutlineDisclosureOptions(style: .header)
            cell.accessories = [.outlineDisclosure(options: headerDisclosureOption)]
        }
        
        dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { (collectionView, indexPath, item) -> UICollectionViewCell? in
            
            switch item {
            case .header(let number):
                return collectionView.dequeueConfiguredReusableCell(using: headerCellRegistration, for: indexPath, item: number)
            case .item(let letter):
                return collectionView.dequeueConfiguredReusableCell(using: itemCellRegistration, for: indexPath, item: letter)
            }
        })
    }

    private func applySnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()

        snapshot.appendSections(groups.map { .group($0) })
        
        // This line causes the error messages. If I comment it out, the messages go away but changing sections (removing or adding some) doesn't work any more (the collectionview does not reflect the changes of the sections)
        dataSource.apply(snapshot, animatingDifferences: false)
        
        for group in groups {
            var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
            
            // header
            let headerItem = Item.header(group)
            sectionSnapshot.append([headerItem])
            
            // items
            sectionSnapshot.append((groupItems[group] ?? []).map { Item.item($0) }, to: headerItem)
            
            sectionSnapshot.expand([headerItem])
            
            dataSource.apply(sectionSnapshot, to: .group(group))
        }
    }

}

这只是一个简单的集合视图,显示几个部分,每个部分有 4 个项目。为了演示我的问题,我添加了一个在加载视图控制器 2 秒后自动调用的闭包。它删除了第一部分并更新了 collectionview 的数据源。

有两个问题:

首先,它给了我这些错误信息:

sectionSnapshotExample[15380:1252802] [DiffableDataSource] Failed to find index of item sectionSnapshotExample.Item.header(1)
sectionSnapshotExample[15380:1252802] [DiffableDataSource] Failed to find index of item sectionSnapshotExample.Item.header(4)
sectionSnapshotExample[15380:1252802] [DiffableDataSource] Failed to find index of item sectionSnapshotExample.Item.header(2)
sectionSnapshotExample[15380:1252802] [DiffableDataSource] Failed to find index of item sectionSnapshotExample.Item.header(3)
sectionSnapshotExample[15380:1252802] [DiffableDataSource] Failed to find index of item sectionSnapshotExample.Item.header(5)

其次,更新在视觉上并不好,因为它重新加载了整个集合视图(所有单元格都在交叉淡入淡出,即使它们没有改变)。

错误是在applySnapshot()方法内部引起的:

private func applySnapshot() {
    var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()

    snapshot.appendSections(groups.map { .group($0) })
    

    // This line causes the error messages
    dataSource.apply(snapshot, animatingDifferences: false)
    
    ....
}

这会将这些部分应用于数据源(Apple 在他们的示例项目中也是如此)。

如果我将此行注释掉,则这些部分将不再更新(在此示例中,已删除的部分将保留在集合视图中。

有任何想法吗?我做错了吗?section snapshots不打算与动态内容一起使用吗?如果是这样,是否有另一种简单的方法来拥有可折叠的部分?

谢谢!

4

0 回答 0