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

Swift 4 中如何在TableViewCell内通过代码创建CollectionView

嘿,这个需求我刚好做过,纯代码实现完全没问题,我一步步给你拆解,保证每个TableViewCell里的CollectionView数量能根据数据动态变化,完全不用碰Storyboard:

第一步:改造你的自定义TableViewCell

首先在你的CustomTableViewCell里,我们需要用数组来管理多个CollectionView,同时处理它们的布局、代理和数据源:

import UIKit

class CustomTableViewCell: UITableViewCell {
    // 存放当前cell内的所有CollectionView
    private var collectionViews: [UICollectionView] = []
    // 二维数组:外层对应每个CollectionView,内层是该CollectionView的数据源
    var cellData: [[String]] = [] {
        didSet {
            // 数据更新时自动刷新所有CollectionView
            collectionViews.forEach { $0.reloadData() }
        }
    }
    
    // 自定义闭包,用来把CollectionView的点击事件转发给TableViewController(可选)
    var onCollectionItemSelected: ((_ collectionTag: Int, _ itemIndex: Int) -> Void)?
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupContentViewConstraints()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 先给contentView设置基础约束,避免后续布局混乱
    private func setupContentViewConstraints() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
            contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
    
    // 核心方法:根据数据数量动态创建/刷新CollectionView
    func configureDynamicCollectionViews() {
        // 先清理旧的CollectionView,避免cell复用时重复添加
        collectionViews.forEach { $0.removeFromSuperview() }
        collectionViews.removeAll()
        
        // 遍历数据源,有多少组数据就创建多少个CollectionView
        for (index, _) in cellData.enumerated() {
            // 创建流式布局,可根据需求调整item大小、滚动方向
            let layout = UICollectionViewFlowLayout()
            layout.itemSize = CGSize(width: 80, height: 80)
            layout.minimumInteritemSpacing = 10
            layout.minimumLineSpacing = 10
            layout.scrollDirection = .horizontal // 改成.vertical就是垂直滚动
            
            // 创建CollectionView
            let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
            collectionView.translatesAutoresizingMaskIntoConstraints = false
            collectionView.backgroundColor = .lightGray.withAlphaComponent(0.2)
            // 注册你的自定义CollectionViewCell
            collectionView.register(CustomCollectionViewCell.self, forCellWithReuseIdentifier: "CustomCollectionCell")
            
            // 设置代理和数据源,直接让当前cell承担
            collectionView.delegate = self
            collectionView.dataSource = self
            // 给每个CollectionView打tag,方便区分是哪个
            collectionView.tag = index
            
            contentView.addSubview(collectionView)
            collectionViews.append(collectionView)
            
            // 设置CollectionView的约束:垂直排列,左右边距10,每个高度80,间距10
            NSLayoutConstraint.activate([
                collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
                collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
                collectionView.heightAnchor.constraint(equalToConstant: 80),
                // 第一个CollectionView贴contentView顶部,后续的贴前一个的底部
                collectionView.topAnchor.constraint(equalTo: index == 0 ? contentView.topAnchor : collectionViews[index-1].bottomAnchor, constant: 10)
            ])
            
            // 最后一个CollectionView贴contentView底部
            if index == cellData.count - 1 {
                NSLayoutConstraint.activate([
                    collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
                ])
            }
        }
    }
}

// 让TableViewCell遵守CollectionView的代理和数据源协议
extension CustomTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // 根据tag获取对应CollectionView的数据源数量
        return cellData[collectionView.tag].count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCollectionCell", for: indexPath) as! CustomCollectionViewCell
        // 给CollectionViewCell设置内容(这里用文本举例,你可以换成自己的UI)
        cell.titleLabel.text = cellData[collectionView.tag][indexPath.item]
        cell.backgroundColor = .white
        return cell
    }
    
    // 处理CollectionView的点击事件,转发给TableViewController
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        onCollectionItemSelected?(collectionView.tag, indexPath.item)
    }
}
第二步:完善你的自定义CollectionViewCell

这里是纯代码实现的CustomCollectionViewCell,你可以根据自己的需求调整UI:

class CustomCollectionViewCell: UICollectionViewCell {
    // 自定义标签,用来显示内容
    let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textAlignment = .center
        label.font = UIFont.systemFont(ofSize: 14)
        label.textColor = .black
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        addSubview(titleLabel)
        NSLayoutConstraint.activate([
            titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
            titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
            titleLabel.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 5),
            titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: trailingAnchor, constant: -5)
        ])
        // 给cell加个圆角,看起来更美观
        layer.cornerRadius = 8
        layer.masksToBounds = true
    }
}
第三步:在TableViewController里配置数据源和复用逻辑

最后在你的CustomTableViewController里,给每个TableViewCell传对应的数据,并处理复用:

import UIKit

class CustomTableViewController: UITableViewController {
    // 模拟数据源:外层是每个TableViewCell的数据,内层是该cell内多个CollectionView的数据源
    private let tableViewDataSource: [[[String]]] = [
        // 第一个TableViewCell包含2个CollectionView
        [["苹果", "香蕉", "橙子"], ["可乐", "雪碧"]],
        // 第二个TableViewCell包含3个CollectionView
        [["红色"], ["正方形", "圆形", "三角形", "五角星"], ["周一", "周二"]],
        // 第三个TableViewCell包含1个CollectionView
        [["篮球", "足球", "排球", "羽毛球", "乒乓球"]]
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 注册自定义TableViewCell
        tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableCell")
        // 开启自动计算cell高度,避免手动计算高度的麻烦
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 250 // 给个预估高度,防止界面跳动
        tableView.separatorStyle = .singleLine
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableViewDataSource.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableCell", for: indexPath) as! CustomTableViewCell
        // 给cell传对应的数据
        cell.cellData = tableViewDataSource[indexPath.row]
        // 调用配置CollectionView的方法
        cell.configureDynamicCollectionViews()
        // 设置点击事件的回调(可选)
        cell.onCollectionItemSelected = { [weak self] collectionTag, itemIndex in
            guard let self = self else { return }
            let selectedData = self.tableViewDataSource[indexPath.row][collectionTag][itemIndex]
            print("你点击了第\(indexPath.row+1)个TableViewCell里的第\(collectionTag+1)个CollectionView,第\(itemIndex+1)个item:\(selectedData)")
            // 这里可以添加你的业务逻辑,比如跳转页面、弹出弹窗等
        }
        return cell
    }
}
关键注意点
  • Cell复用问题:每次调用configureDynamicCollectionViews时,一定要先移除旧的CollectionView,否则复用cell时会重复添加,导致布局混乱。
  • 自动高度:TableView开启automaticDimension后,会根据cell内的CollectionView自动计算高度,不需要手动设置。
  • 代理转发:如果需要在TableViewController处理CollectionView的交互,用闭包是最简洁的方式,避免复杂的代理嵌套。

这样整个流程就完成了,完全纯代码实现,每个TableViewCell里的CollectionView数量会根据你传入的cellData自动变化,完美满足你的需求!

内容的提问来源于stack exchange,提问作者Sam H

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:55:36