嵌套UITableViewCell的UITableView动态行高实现难题
嘿,这个场景我太熟了——嵌套TableView动态调整行高,核心就是要让外层的master tableView能精准拿到内层child tableView的真实内容高度,然后实时更新自身cell的高度。下面给你一步步拆解解决方案:
核心思路
问题的关键在于:master tableView初始化时,child tableView可能还没加载完数据,导致无法拿到真实高度。我们需要做的是在child tableView数据就绪后,把它的内容高度传递给master tableView,触发master tableView重新计算对应cell的行高。
1. 给嵌套Child TableView的自定义Cell添加高度回调
首先在你的自定义Cell(包含child tableView的那个)里,定义一个闭包用来传递高度。同时要禁用child tableView的滚动,确保它的内容全部展开,这样contentSize才能准确反映总高度:
class ChildContainerCell: UITableViewCell { @IBOutlet weak var childTableView: UITableView! // 用来传递child tableView内容高度的闭包 var contentHeightUpdated: ((CGFloat) -> Void)? private var childData: [YourDataModel] = [] override func awakeFromNib() { super.awakeFromNib() // 配置child tableView的数据源/代理(这里假设Cell自身担任代理) childTableView.dataSource = self childTableView.delegate = self // 关键设置:禁用滚动,让内容全部展开 childTableView.isScrollEnabled = false childTableView.showsVerticalScrollIndicator = false // 如果child tableView是动态行高,必须设置预估高度 childTableView.rowHeight = UITableView.automaticDimension childTableView.estimatedRowHeight = 44 } // 外部调用这个方法给child tableView传数据 func setup(with data: [YourDataModel]) { self.childData = data childTableView.reloadData() // 数据加载完成后,计算并传递真实高度 let realHeight = childTableView.contentSize.height contentHeightUpdated?(realHeight) } } // 实现child tableView的数据源方法 extension ChildContainerCell: UITableViewDataSource, UITableViewDelegate { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return childData.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ChildCell", for: indexPath) as! ChildCell cell.setup(with: childData[indexPath.row]) return cell } }
2. 在Master ViewController中处理高度更新
在master tableView的cellForRowAt方法里,绑定Cell的高度回调,把得到的真实高度存储起来,然后触发master tableView更新对应行的高度:
class MasterVC: UITableViewController { // 存储每个Cell的真实高度,用row作为key private var cellHeightCache: [Int: CGFloat] = [:] private var masterData: [[YourDataModel]] = [] // 外层数据源,每个元素对应一个child tableView的数据 override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ChildContainerCell", for: indexPath) as! ChildContainerCell // 给Cell传递对应的数据 let childData = masterData[indexPath.row] cell.setup(with: childData) // 绑定高度回调 cell.contentHeightUpdated = { [weak self] height in guard let self = self else { return } // 缓存当前Cell的真实高度 self.cellHeightCache[indexPath.row] = height // 触发master tableView重新计算行高 self.tableView.beginUpdates() self.tableView.endUpdates() } return cell } // 告诉master tableView每行的高度:优先用缓存的真实高度,没有则用预估高度 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return cellHeightCache[indexPath.row] ?? 200 // 200是你xib的原始高度,作为预估值 } override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return cellHeightCache[indexPath.row] ?? 200 } }
3. 解决初始化时的时机问题
上面的方案已经覆盖了初始化场景:当master tableView首次加载Cell时,调用setup(with:)会触发child tableView reloadData,随后计算真实高度并通过回调通知master tableView更新行高。即使初始时master用了预估高度,等child数据就绪后会自动调整到真实高度。
额外注意事项
- 如果child tableView的内容会动态变化(比如新增/删除行),要在内容变更后重新计算高度并触发
contentHeightUpdated回调。 - 如果child tableView是固定行高,也可以直接提前计算高度(行数×行高+内边距),不用依赖contentSize,这样初始化时就能直接拿到准确高度:
// 在ChildContainerCell的setup方法里替换高度计算逻辑 let fixedRowHeight: CGFloat = 44 let realHeight = CGFloat(childData.count) * fixedRowHeight + childTableView.contentInset.top + childTableView.contentInset.bottom contentHeightUpdated?(realHeight)
内容的提问来源于stack exchange,提问作者knart
相关产品推荐
相关产品推荐

