UiKit中使用DiffableDataSource进行数据差异化更新
文章目录
- 一、前言
- 二、示例代码
一、前言
在使用UICollectionView
的过程中,使用DiffableDataSource
进行差异化数据更新,可以更好的优化性能,并且简化数据更新时候的动画效果。但是代码有一点问题,是关于并发的问题时候使用了nonisolated
进行解决,暂时不知道更好的方式
二、示例代码
import UIKit// MARK: - 类型定义(确保在任何类之外定义)
enum HorizontalSection: String, Hashable, Sendable {case main
}// 定义数据模型,需要遵循 Hashable 和 Sendable
nonisolated struct HorizontalItem: Hashable, Sendable {let id: UUID = UUID()let title: Stringinit(title: String) {self.title = title}
}class HorizontalScrollCollectionView: UIView {// MARK: - Propertiesprivate var collectionView: UICollectionView!// ✅ 使用 lazy var 方式定义 DiffableDataSource(推荐方式)private var diffableDataSource: UICollectionViewDiffableDataSource<HorizontalSection, HorizontalItem>!var clickItemCall: ((Int) -> Void)?// ✅ Cell Registration - 使用现代化的注册方式private let cellRegistration: UICollectionView.CellRegistration<ColorCell, HorizontalItem> = {UICollectionView.CellRegistration<ColorCell, HorizontalItem> { cell, indexPath, item incell.configure(with: indexPath.item)}}()// MARK: - Initializationoverride init(frame: CGRect) {super.init(frame: frame)setupCollectionView()loadData()}required init?(coder: NSCoder) {super.init(coder: coder)setupCollectionView()loadData()}private func setupCollectionView() {let layout = UICollectionViewFlowLayout()layout.scrollDirection = .horizontallayout.minimumLineSpacing = 26layout.minimumInteritemSpacing = 10layout.itemSize = CGSize(width: 100, height: 150)collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)collectionView.translatesAutoresizingMaskIntoConstraints = falsecollectionView.backgroundColor = .clearcollectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]collectionView.showsHorizontalScrollIndicator = falsecollectionView.decelerationRate = .fastcollectionView.delegate = selfaddSubview(collectionView)NSLayoutConstraint.activate([collectionView.topAnchor.constraint(equalTo: topAnchor),collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),collectionView.bottomAnchor.constraint(equalTo: bottomAnchor)])// ✅ collectionView 已经存在,可以安全创建 diffableDataSourcediffableDataSource = UICollectionViewDiffableDataSource<HorizontalSection, HorizontalItem>(collectionView: collectionView) { [weak self] collectionView, indexPath, item inguard let self = self else { return UICollectionViewCell() }return collectionView.dequeueConfiguredReusableCell(using: self.cellRegistration,for: indexPath,item: item)}}// MARK: - Data Loadingprivate func loadData() {var items: [HorizontalItem] = []for index in 0..<10 {items.append(HorizontalItem(title: "this is item \(index)"))}// ✅ 使用 Snapshot 更新数据applySnapshot(items: items, animatingDifferences: false)}// MARK: - Apply Snapshot/// 应用快照更新数据/// - Parameters:/// - items: 要显示的数据项/// - animatingDifferences: 是否使用动画private func applySnapshot(items: [HorizontalItem], animatingDifferences: Bool = true) {var snapshot = NSDiffableDataSourceSnapshot<HorizontalSection, HorizontalItem>()snapshot.appendSections([HorizontalSection.main])snapshot.appendItems(items, toSection: HorizontalSection.main)diffableDataSource.apply(snapshot, animatingDifferences: animatingDifferences)}// MARK: - Public Methods/// 更新数据的公共方法func updateItems(_ newItems: [HorizontalItem], animated: Bool = true) {applySnapshot(items: newItems, animatingDifferences: animated)}/// 添加单个项目func addItem(_ item: HorizontalItem, animated: Bool = true) {var snapshot = diffableDataSource.snapshot()snapshot.appendItems([item], toSection: HorizontalSection.main)diffableDataSource.apply(snapshot, animatingDifferences: animated)}/// 删除指定项目func removeItem(_ item: HorizontalItem, animated: Bool = true) {var snapshot = diffableDataSource.snapshot()snapshot.deleteItems([item])diffableDataSource.apply(snapshot, animatingDifferences: animated)}/// 移动项目位置func moveItem(_ item: HorizontalItem, to destinationItem: HorizontalItem, animated: Bool = true) {var snapshot = diffableDataSource.snapshot()snapshot.moveItem(item, afterItem: destinationItem)diffableDataSource.apply(snapshot, animatingDifferences: animated)}/// 获取当前所有项目func getAllItems() -> [HorizontalItem] {return diffableDataSource.snapshot().itemIdentifiers}/// 获取指定 indexPath 的项目func getItem(at indexPath: IndexPath) -> HorizontalItem? {return diffableDataSource.itemIdentifier(for: indexPath)}
}// MARK: - UICollectionViewDelegate
extension HorizontalScrollCollectionView: UICollectionViewDelegate {func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {print("选中了第 \(indexPath.item) 个 item")// ✅ 通过 diffableDataSource 获取对应的 itemif let item = diffableDataSource.itemIdentifier(for: indexPath) {print("选中的 item 标题: \(item.title)")print("选中的 item ID: \(item.id)")}clickItemCall?(indexPath.item)}
}// MARK: - 使用示例
/*使用方式:// 1. 创建视图let horizontalView = HorizontalScrollCollectionView()view.addSubview(horizontalView)// 2. 设置点击回调horizontalView.clickItemCall = { index inprint("点击了第 \(index) 个项目")}// 3. 更新数据let newItems = [HorizontalItem(title: "新项目 1"),HorizontalItem(title: "新项目 2"),HorizontalItem(title: "新项目 3")]horizontalView.updateItems(newItems, animated: true)// 4. 添加单个项目let newItem = HorizontalItem(title: "新增项目")horizontalView.addItem(newItem, animated: true)// 5. 删除项目if let itemToRemove = horizontalView.getItem(at: IndexPath(item: 0, section: 0)) {horizontalView.removeItem(itemToRemove, animated: true)}*/