基于Auto Layout实现单个视图中两个UITableView的动态高度调整
解决单个ViewController中两个UITableView的动态高度适配问题
嘿,这个需求我之前做项目时刚好碰到过,要实现「尽量让表格不滚动,总高度超屏时等高」的效果,其实通过动态计算内容高度+约束优先级调整就能搞定,下面一步步来实现:
一、先做好基础约束配置
给两个UITableView设置好基础布局:
- 给第一个tableView加上顶部约束(比如和导航栏底部对齐)、左右边缘约束;
- 给第二个tableView加上底部约束(比如和tabBar顶部对齐)、左右边缘约束;
- 在两个tableView之间添加固定间距的约束;
- 给每个tableView添加一个高度约束,并把这两个约束的引用保存到代码里(比如叫
tableView1HeightConstraint和tableView2HeightConstraint),初始值可以随便设,后面会动态修改。
二、实现内容高度的准确计算
要让tableView的高度适配内容,首先得能算出它的实际内容高度。这里写个工具方法,注意要在tableView刷新数据后调用,确保contentSize是最新的:
private func calculateTableViewContentHeight(_ tableView: UITableView) -> CGFloat { // 先强制更新布局,保证contentSize准确 tableView.layoutIfNeeded() // 用sizeThatFits来获取最合适的高度,比直接取contentSize更可靠(尤其是带表头表尾的情况) let fittingSize = CGSize(width: tableView.bounds.width, height: UIView.layoutFittingCompressedSize.height) return tableView.sizeThatFits(fittingSize).height }
注意:要确保你的tableView已经开启了自动行高,也就是设置了
tableView.rowHeight = UITableView.automaticDimension和tableView.estimatedRowHeight = 80(值随便设个合理的就行),否则contentSize会不准确。
三、动态调整高度逻辑
在合适的时机(比如view加载完成、tableView数据刷新后)调用下面的方法,来判断是让两个tableView各自适配内容,还是强制等高:
private func updateTableViewHeights() { // 先算出当前屏幕的可用高度(减去导航栏、tabBar的高度,根据你的实际布局调整) let navBarHeight = navigationController?.navigationBar.frame.height ?? 0 let tabBarHeight = tabBarController?.tabBar.frame.height ?? 0 let availableScreenHeight = view.bounds.height - navBarHeight - tabBarHeight // 计算两个tableView的内容高度 let tableView1ContentHeight = calculateTableViewContentHeight(tableView1) let tableView2ContentHeight = calculateTableViewContentHeight(tableView2) // 两个tableView的总高度(加上中间的间距) let totalContentHeight = tableView1ContentHeight + tableView2ContentHeight + tableViewSpacingConstraint.constant if totalContentHeight <= availableScreenHeight { // 总高度没超过屏幕,让每个tableView适配自己的内容高度 tableView1HeightConstraint.constant = tableView1ContentHeight tableView2HeightConstraint.constant = tableView2ContentHeight // 设置高优先级,确保约束生效 tableView1HeightConstraint.priority = .defaultHigh tableView2HeightConstraint.priority = .defaultHigh // 关闭滚动,因为内容已经完全显示了 tableView1.isScrollEnabled = false tableView2.isScrollEnabled = false } else { // 总高度超屏了,让两个tableView等高 let equalHeight = (availableScreenHeight - tableViewSpacingConstraint.constant) / 2 tableView1HeightConstraint.constant = equalHeight tableView2HeightConstraint.constant = equalHeight // 设置最高优先级,强制等高 tableView1HeightConstraint.priority = .required tableView2HeightConstraint.priority = .required // 开启滚动,方便查看超出的内容 tableView1.isScrollEnabled = true tableView2.isScrollEnabled = true } // 强制更新布局,让约束生效 view.layoutIfNeeded() }
你可以在这些地方调用这个方法:
viewDidLayoutSubviews(确保布局变化时更新)- 每次给tableView刷新数据后(比如
tableView.reloadData()之后)
四、额外优化点
- 如果你的tableView有表头或表尾,上面的
calculateTableViewContentHeight方法已经通过sizeThatFits自动考虑了,不用额外处理; - 要是你用的是Objective-C,逻辑是一样的,只是语法转换一下就行;
- 可以给tableView添加
contentSize的监听(用KVO),当内容变化时自动调用updateTableViewHeights,这样更灵活。
内容的提问来源于stack exchange,提问作者Luis Gómez
相关产品推荐
相关产品推荐

