基于diffable data source实现tableViewCell Xib内嵌collectionView的问题
问题修复方案
1. 统一类型定义,适配Diffable数据源要求
Diffable 数据源要求所有 Section 和 Item 类型遵守Hashable协议,你需要先统一包装嵌套 CollectionView 的数据类型,兼容男士/女士两种数据模型:
// CollectionView 分区枚举 public enum CollectionSection: Hashable { case main } // 包装 CollectionItem 兼容两种数据模型 public enum CollectionItem: Hashable { case mens(Mens) case womens(Womens) } // 原有模型补全 Hashable 协议 extension Mens: Hashable {} extension Womens: Hashable {} extension Product: Hashable {}
2. 修复外层 TableViewController 逻辑
调整数据源初始化顺序,在配置 TableCell 时直接传递当前行对应的 Collection 数据源,避免 Cell 内部持有全量冗余数据:
public enum CategoriesSection: Hashable { case main } class TableCollectionCell: UIViewController { @IBOutlet weak var tableView: UITableView! // Table 行对应分类,0=男士,1=女士 var categoryList: [String] = ["男士", "女士"] var mensList: [Mens] = [] var womensList: [Womens] = [] var datasource: UITableViewDiffableDataSource<CategoriesSection, String>! override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self // 提前注册Cell tableView.register( UINib(nibName: String(describing: CategoiresCell.self), bundle: .main), forCellReuseIdentifier: String(describing: CategoiresCell.self) ) // 先创建数据源再赋值给tableView createDataSource() // 数据请求完成后调用刷新 createSnapshot() } } extension TableCollectionCell: UITableViewDelegate { func createDataSource() { datasource = UITableViewDiffableDataSource( tableView: tableView, cellProvider: { [weak self] (tableview, indexPath, categoryName) -> UITableViewCell? in guard let self = self, let cell = tableview.dequeueReusableCell( withIdentifier: String(describing: CategoiresCell.self), for: indexPath ) as? CategoiresCell else { return UITableViewCell() } // 传递当前行所需数据 cell.name.text = categoryName switch indexPath.row { case 0: cell.currentItemList = self.mensList.map { CollectionItem.mens($0) } case 1: cell.currentItemList = self.womensList.map { CollectionItem.womens($0) } default: break } // 触发Cell内部Collection刷新 cell.reloadCollection() return cell } ) tableView.dataSource = datasource } func createSnapshot() { var snapshot = NSDiffableDataSourceSnapshot<CategoriesSection, String>() snapshot.appendSections([.main]) snapshot.appendItems(categoryList) datasource.apply(snapshot, animatingDifferences: true) } }
3. 修复嵌套CollectionView的TableCell逻辑
调整CollectionView数据源类型,补全Snapshot渲染逻辑:
class CategoiresCell: UITableViewCell { @IBOutlet weak var name: UILabel! @IBOutlet weak var collectionView: UICollectionView! // 接收父视图传递的当前行所有渲染数据 var currentItemList: [CollectionItem] = [] var datasource: UICollectionViewDiffableDataSource<CollectionSection, CollectionItem>? override func awakeFromNib() { super.awakeFromNib() collectionView.delegate = self // 提前注册CollectionCell collectionView.register( UINib(nibName: String(describing: MensCell.self), bundle: .main), forCellWithReuseIdentifier: String(describing: MensCell.self) ) collectionView.register( UINib(nibName: String(describing: WomensCell.self), bundle: .main), forCellWithReuseIdentifier: String(describing: WomensCell.self) ) // 提前初始化Collection数据源 createDataSource() } // 对外暴露刷新方法 func reloadCollection() { createSnapshot() } } extension CategoiresCell: UICollectionViewDelegate { func createDataSource() { datasource = UICollectionViewDiffableDataSource( collectionView: collectionView, cellProvider: { (collectionview, indexPath, item) -> UICollectionViewCell? in switch item { case .mens(let model): guard let cell = collectionview.dequeueReusableCell( withReuseIdentifier: String(describing: MensCell.self), for: indexPath ) as? MensCell else { return UICollectionViewCell() } cell.getData = model return cell case .womens(let model): guard let cell = collectionview.dequeueReusableCell( withReuseIdentifier: String(describing: WomensCell.self), for: indexPath ) as? WomensCell else { return UICollectionViewCell() } cell.getData = model return cell } } ) collectionView.dataSource = datasource } func createSnapshot() { var snapshot = NSDiffableDataSourceSnapshot<CollectionSection, CollectionItem>() snapshot.appendSections([.main]) snapshot.appendItems(currentItemList) datasource?.apply(snapshot, animatingDifferences: true) } }
核心优化说明
- 用枚举包装多类型Item,符合Diffable数据源的强类型要求,避免硬编码行号判断数据类型的耦合逻辑
- 调整数据源初始化顺序,避免空数据源赋值给列表导致的无渲染问题
- 父视图仅传递当前行所需的最小数据集,Cell内部不需要持有全量冗余数据,逻辑更清晰易维护
内容的提问来源于stack exchange,提问作者Faheem
相关产品推荐
相关产品推荐

