如何创建可复用TableView实现订单应用的两种商品类型
订单应用商品类型复用实现方案
基于你已实现的Type One,通过组件化拆分+嵌套TableView的方式复用逻辑和视图,完全避免重复代码,具体实现步骤如下:
1. 抽象统一数据模型
先把两种商品的共性和差异抽象成层级模型,让主TableView能统一处理:
- 基类
BaseGoodsModel:定义通用属性(如商品ID)和通用方法(如返回对应Cell类型) NormalGoodsModel(对应Type One):继承基类,添加描述、规格、选项等专属属性,指定对应的Cell类型CombinationGoodsModel(对应Type Two):继承基类,添加顶部商品类型标题,以及存储嵌套普通商品的数组属性
class BaseGoodsModel { var goodsId: String var cellType: UITableViewCell.Type { return BaseGoodsCell.self } init(goodsId: String) { self.goodsId = goodsId } } class NormalGoodsModel: BaseGoodsModel { var desc: String var spec: String var options: [String] override var cellType: UITableViewCell.Type { return NormalGoodsCell.self } init(goodsId: String, desc: String, spec: String, options: [String]) { self.desc = desc self.spec = spec self.options = options super.init(goodsId: goodsId) } } class CombinationGoodsModel: BaseGoodsModel { var typeTitle: String var nestedGoodsList: [NormalGoodsModel] override var cellType: UITableViewCell.Type { return CombinationGoodsCell.self } init(goodsId: String, typeTitle: String, nestedGoodsList: [NormalGoodsModel]) { self.typeTitle = typeTitle self.nestedGoodsList = nestedGoodsList super.init(goodsId: goodsId) } }
2. 复用Type One的Cell与业务逻辑
你已实现的NormalGoodsCell直接保留,同时把Type One的交互逻辑(比如选项选择、规格变更)抽离到NormalGoodsModel的扩展或独立工具类中,避免和Cell耦合,方便后续嵌套时调用。
3. 实现组合商品Cell(嵌套TableView)
创建CombinationGoodsCell,顶部放商品类型标题,下方嵌套一个小型TableView,这个嵌套TableView直接复用NormalGoodsCell和对应的数据源逻辑:
class CombinationGoodsCell: UITableViewCell { private lazy var typeTitleLabel: UILabel = { let label = UILabel() label.font = UIFont.boldSystemFont(ofSize: 16) label.textColor = .black return label }() private lazy var nestedTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .plain) tableView.register(NormalGoodsCell.self, forCellReuseIdentifier: "NormalGoodsCell") tableView.delegate = self tableView.dataSource = self tableView.isScrollEnabled = false // 禁用嵌套滚动,避免和主TableView冲突 tableView.separatorStyle = .none return tableView }() private var goodsList: [NormalGoodsModel] = [] override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupSubviews() { contentView.addSubview(typeTitleLabel) contentView.addSubview(nestedTableView) typeTitleLabel.snp.makeConstraints { make in make.top.left.right.equalToSuperview().inset(16) make.height.equalTo(24) } nestedTableView.snp.makeConstraints { make in make.top.equalTo(typeTitleLabel.snp.bottom).offset(12) make.left.right.bottom.equalToSuperview().inset(16) } } func configure(with model: CombinationGoodsModel) { typeTitleLabel.text = model.typeTitle goodsList = model.nestedGoodsList nestedTableView.reloadData() // 计算嵌套TableView总高度并更新约束 let totalHeight = goodsList.reduce(0) { $0 + $1.cellHeight } nestedTableView.snp.updateConstraints { make in make.height.equalTo(totalHeight) } } } // 嵌套TableView代理/数据源,直接复用Type One的逻辑 extension CombinationGoodsCell: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return goodsList.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "NormalGoodsCell", for: indexPath) as! NormalGoodsCell cell.configure(with: goodsList[indexPath.row]) // 通过闭包把嵌套Cell的交互事件传递给主Controller cell.onOptionSelected = { [weak self] option in self?.nestedOptionSelected?(option) } return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return goodsList[indexPath.row].cellHeight } // 定义闭包传递嵌套Cell的交互事件 var nestedOptionSelected: ((String) -> Void)? }
4. 主TableView统一管理两种商品类型
主TableView的数据源数组用[BaseGoodsModel]类型,在代理方法中根据模型的cellType创建对应Cell,实现统一渲染:
class OrderViewController: UIViewController { private lazy var tableView: UITableView = { let tableView = UITableView(frame: view.bounds, style: .grouped) tableView.register(NormalGoodsCell.self, forCellReuseIdentifier: "NormalGoodsCell") tableView.register(CombinationGoodsCell.self, forCellReuseIdentifier: "CombinationGoodsCell") tableView.delegate = self tableView.dataSource = self return tableView }() private var goodsData: [BaseGoodsModel] = [] override func viewDidLoad() { super.viewDidLoad() view.addSubview(tableView) loadMockData() } private func loadMockData() { // 模拟普通商品数据 let normalGoods = NormalGoodsModel(goodsId: "1", desc: "普通商品描述", spec: "规格:中号", options: ["选项A", "选项B"]) // 模拟组合商品数据 let comboGoods = CombinationGoodsModel(goodsId: "2", typeTitle: "组合套餐A", nestedGoodsList: [normalGoods]) goodsData = [normalGoods, comboGoods] tableView.reloadData() } } extension OrderViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return goodsData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let model = goodsData[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: model.cellType), for: indexPath) if let normalCell = cell as? NormalGoodsCell { normalCell.configure(with: model as! NormalGoodsModel) normalCell.onOptionSelected = { option in // 处理普通商品选项选择 print("选中普通商品选项:\(option)") } } else if let comboCell = cell as? CombinationGoodsCell { comboCell.configure(with: model as! CombinationGoodsModel) comboCell.nestedOptionSelected = { option in // 处理组合商品嵌套选项选择 print("选中组合商品嵌套选项:\(option)") } } return cell } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let model = goodsData[indexPath.row] if let comboModel = model as? CombinationGoodsModel { // 组合商品Cell高度 = 标题高度 + 嵌套商品总高度 + 内边距 return 24 + 12 + comboModel.nestedGoodsList.reduce(0) { $0 + $1.cellHeight } + 16 } return (model as! NormalGoodsModel).cellHeight } }
5. 优化建议
- 把Cell高度计算逻辑封装到对应模型中,避免Controller中出现大量高度计算代码
- 统一通过闭包或代理传递交互事件,保证业务逻辑集中在主Controller处理
- 若需动态高度,主TableView和嵌套TableView均可使用
UITableView.automaticDimension,前提是Cell的约束设置完整
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

