UITableView嵌套UICollectionView再嵌套UITableView高度自适应失效问题
问题原因及解决方案
核心遗漏点
你遇到的是多层滚动嵌套下的自适应高度失效问题,核心漏了3个配置:
- UICollectionView的
isScrollEnabled没有设为false,只有关闭滚动,UICollectionView才会参考intrinsicContentSize计算自身高度,否则会优先按照约束的固定高度/父视图高度渲染 - 你修改了外层View的
intrinsicContentSize,但没有在修改后触发外层父视图的布局刷新链:内层红色tableView折叠/展开后,要先同步更新当前显示的UICollectionViewCell的高度,再把最大的cell高度同步给UICollectionView的intrinsicContentSize,最后触发外层黄色UITableView的行高刷新 - 你没有监听UICollectionViewCell内部红色tableView的contentSize变化,导致高度变化的信号没有向上传递到外层View
具体修复步骤
- UICollectionView基础配置调整
给UICollectionView添加如下配置:
collectionView.isScrollEnabled = false collectionView.heightAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
同时自定义UICollectionView子类,重写intrinsicContentSize:
class AutoHeightCollectionView: UICollectionView { override var contentSize: CGSize { didSet { invalidateIntrinsicContentSize() } } override var intrinsicContentSize: CGSize { layoutIfNeeded() // 取当前所有可见cell的最大高度作为collectionView的高度 let maxCellHeight = visibleCells.map { $0.frame.height }.max() ?? 0 return CGSize(width: contentSize.width, height: maxCellHeight) } }
- 高度变化信号向上传递
在UICollectionViewCell的自定义类里,监听内部红色tableView的contentSize变化:
class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var redTableView: UITableView! var onHeightChange: ((CGFloat) -> Void)? override func awakeFromNib() { super.awakeFromNib() redTableView.addObserver(self, forKeyPath: "contentSize", options: [.new], context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "contentSize", let newSize = change?[.newKey] as? CGSize { // 更新当前cell的高度约束 redTableView.heightAnchor.constraint(equalToConstant: newSize.height).isActive = true onHeightChange?(newSize.height) } } }
- 触发外层布局刷新
在持有UICollectionView的外层View里,接收cell的高度变化回调,更新collectionView的布局后,触发外层黄色tableView的行高刷新:
// collectionView的cellForItemAt里配置回调 cell.onHeightChange = { [weak self] height in guard let self = self else { return } self.collectionView.invalidateIntrinsicContentSize() // 找到外层的黄色tableView,刷新对应行 if let superTableView = self.superview?.superview as? UITableView, let indexPath = superTableView.indexPath(for: self.superview as! UITableViewCell) { superTableView.reloadRows(at: [indexPath], with: .none) } }
- 黄色UITableView行高配置
外层黄色tableView要开自动行高:
tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 // 填你预估的最小行高即可
内容的提问来源于stack exchange,提问作者Praveenkumar
相关产品推荐
相关产品推荐

