You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 06:24:03