1

所以我有一个带有 Diffable DataSource List CollectionView 的屏幕,当 ManagedContext 发生更改时,它将重新加载。

我现在重新加载的问题是单元格一直在随机变化,并且大多数时候它们会用来自不同部分的项目复制自己。

什么是重新加载这个的好解决方案?

class MultiSectionExpandableListView: UIView {
weak var delegate: MultiSectionExpandableListViewDelegate?
private var objects: [HeaderItem]
private var collectionView: UICollectionView!
private var dataSource: UICollectionViewDiffableDataSource<HeaderItem, ItemType>!
private lazy var noEntriesView: NoEntriesView = {
    let view = NoEntriesView.instantiateNib()
    view.frame = collectionView.bounds
    return view
}()

init(frame: CGRect, objects: [HeaderItem]) {
    self.objects = objects
    super.init(frame: frame)

    setupCollectionView()
}
    
required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func setupCollectionView() {
    var layoutConfig = UICollectionLayoutListConfiguration(appearance: .sidebar)
    layoutConfig.headerMode = .firstItemInSection
    layoutConfig.showsSeparators = false
    layoutConfig.backgroundColor = .clear
    let listLayout = UICollectionViewCompositionalLayout.list(using: layoutConfig)
        
    collectionView = UICollectionView(frame: bounds, collectionViewLayout: listLayout)
    collectionView.delegate = self
    collectionView.backgroundColor = .clear
    addSubview(collectionView)

    collectionView.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        collectionView.topAnchor.constraint(equalTo: topAnchor),
        collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
        collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
        collectionView.bottomAnchor.constraint(equalTo: bottomAnchor)])

    guard objects[0].items.count > 0 || objects[1].items.count > 0 || objects[2].items.count > 0 || objects[3].items.count > 0 else {
        collectionView.backgroundView = noEntriesView
        return
    }
    
    let headerCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, HeaderItem> {
        (cell, indexPath, headerItem) in
        var content = cell.defaultContentConfiguration()
        content.text = headerItem.title
        cell.contentConfiguration = content
        
        var headerDisclosureOption = UICellAccessory.OutlineDisclosureOptions(style: .header)
        headerDisclosureOption.tintColor = AppStorage.tintColor.uiColor()
        cell.accessories = [.outlineDisclosure(options:headerDisclosureOption)]
    }

    let itemCellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, ListItem> {
        (cell, indexPath, listItem) in
        
        var configuration = cell.defaultContentConfiguration()
        configuration.text = listItem.title
        configuration.secondaryText = listItem.description
        configuration.imageProperties.maximumSize = CGSize(width: 50, height: 50)
        configuration.imageProperties.reservedLayoutSize = CGSize(width: 60, height: 60)
        let spinner = UIActivityIndicatorView(style: .large)
        spinner.color = AppStorage.tintColor.uiColor()
        spinner.startAnimating()
        cell.accessories = [.customView(configuration: UICellAccessory.CustomViewConfiguration(customView: spinner, placement: .leading(displayed: .always)))]
        cell.contentConfiguration = configuration
        
        DownloadImageService.shared.loadImage(stringUrl: listItem.imageURL) { (data, error) in
            guard error == nil, let data = data else { return }
            configuration.image = UIImage(data: data)
            cell.accessories = []
            cell.contentConfiguration = configuration
        }
    }

    dataSource = UICollectionViewDiffableDataSource<HeaderItem, ItemType>(collectionView: collectionView) {
        (collectionView, indexPath, itemType) -> UICollectionViewCell? in
        switch itemType {
        case .header(let headerItem):
            let cell = collectionView.dequeueConfiguredReusableCell(using: headerCellRegistration, for: indexPath, item: headerItem)
            return cell
        case .cell(let listItem):
            let cell = collectionView.dequeueConfiguredReusableCell(using: itemCellRegistration, for: indexPath, item: listItem)
            cell.backgroundConfiguration?.backgroundColor = .clear
            return cell
        }
    }

    var currentSnapshot = NSDiffableDataSourceSnapshot<HeaderItem, ItemType>()
    currentSnapshot.appendSections(objects)
    dataSource.apply(currentSnapshot)

    for headerItem in objects {
        var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<ItemType>()
        let headerListItem = ItemType.header(headerItem)
        sectionSnapshot.append([headerListItem])

        let listItemArray = headerItem.items.map { ItemType.cell($0) }
        sectionSnapshot.append(listItemArray, to: headerListItem)
        sectionSnapshot.expand([headerListItem])

        dataSource.apply(sectionSnapshot, to: headerItem, animatingDifferences: false)
    }
}

func reloadCollectionView(with newObjects: [HeaderItem]) {
    objects = newObjects
    guard objects[0].items.count > 0 || objects[1].items.count > 0 || objects[2].items.count > 0 || objects[3].items.count > 0 else {
        collectionView.backgroundView = noEntriesView
        return
    }
    var currentSnapshot = dataSource.snapshot()
    currentSnapshot = NSDiffableDataSourceSnapshot<HeaderItem, ItemType>()
    currentSnapshot.appendSections(objects)
    dataSource.apply(currentSnapshot)
    for headerItem in objects {
        var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<ItemType>()
        let headerListItem = ItemType.header(headerItem)
        sectionSnapshot.append([headerListItem])

        let listItemArray = headerItem.items.map { ItemType.cell($0) }
        sectionSnapshot.append(listItemArray, to: headerListItem)
        sectionSnapshot.expand([headerListItem])

        dataSource.apply(sectionSnapshot, to: headerItem, animatingDifferences: false)
    }
}

}

还有我的对象:

struct ListItem: Hashable {
    let title: String
    let description: String
    let imageURL: String
}

struct HeaderItem: Hashable {
    let title: String
    let items: [ListItem]
}

enum ItemType: Hashable {
    case header(HeaderItem)
    case cell(ListItem)
}
4

0 回答 0