Autolayout约束致内容溢出视图,tableHeaderView自适应高度问题求助
问题描述


本问题会根据各位建议持续更新,但目前所有小调整建议均未解决问题,情况依旧。
问题概述
- 我有一个ViewController(secondVC),其中包含tableView(secondTableView)。
- 尝试在该tableView顶部添加tableHeaderView(橙色背景,如附图所示)。
- 想在tableHeaderView中嵌套上下排列的两个Label(labelOne在上,labelTwo在下)。
- labelOne为静态内容,固定显示"USER:",不会更改。
- labelTwo为动态内容,会发生变化,需占用所需行数,且tableHeaderView高度需自适应以容纳labelOne和labelTwo的长文本内容。
但目前如图片所示,labelOne完全消失,labelTwo布局异常。我猜测是约束设置有误,恳请帮忙修正约束。
现有代码
import UIKit class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var secondTableView: UITableView! var stuff = [("Some other stuff 1"),("Some other stuff 2"),("Some other stuff 3")] var insertionText: String = "" override func viewDidLoad() { super.viewDidLoad() secondTableView.delegate = self secondTableView.dataSource = self let tableHeader = UIView() secondTableView.tableHeaderView = tableHeader tableHeader.backgroundColor = .systemOrange tableHeader.translatesAutoresizingMaskIntoConstraints = false let size = tableHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) let height = size.height let width = size.width tableHeader.frame = CGRectMake(0, 0, width, height) let labelOne = UILabel(frame: tableHeader.bounds) labelOne.text = "USER:" labelOne.numberOfLines = 0 labelOne.lineBreakMode = .byWordWrapping labelOne.textAlignment = .center labelOne.translatesAutoresizingMaskIntoConstraints = false let labelTwo = UILabel(frame: tableHeader.bounds) labelTwo.translatesAutoresizingMaskIntoConstraints = false labelTwo.text = "Inherited text here: \(insertionText)" labelTwo.numberOfLines = 0 labelTwo.lineBreakMode = .byWordWrapping labelTwo.textAlignment = .center labelTwo.translatesAutoresizingMaskIntoConstraints = false tableHeader.addSubview(labelOne) tableHeader.addSubview(labelTwo) labelOne.topAnchor.constraint( equalTo: tableHeader.layoutMarginsGuide.topAnchor, constant: 11).isActive = true labelOne.leadingAnchor.constraint( equalTo: tableHeader.layoutMarginsGuide.leadingAnchor, constant: 20).isActive = true labelOne.trailingAnchor.constraint( equalTo: tableHeader.layoutMarginsGuide.trailingAnchor, constant: -20).isActive = true labelTwo.topAnchor.constraint( equalTo: labelOne.layoutMarginsGuide.bottomAnchor, constant: 0).isActive = true labelTwo.leadingAnchor.constraint( equalTo: tableHeader.leadingAnchor, constant: 20).isActive = true labelTwo.trailingAnchor.constraint( equalTo: tableHeader.trailingAnchor, constant: -20).isActive = true labelTwo.bottomAnchor.constraint( equalTo: tableHeader.bottomAnchor, constant: -11).isActive = true } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if let tableHeader = secondTableView.tableHeaderView { let height = tableHeader.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height var headerFrame = tableHeader.frame if height != headerFrame.size.height { headerFrame.size.height = height tableHeader.frame = headerFrame secondTableView.tableHeaderView = tableHeader } } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { stuff.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = secondTableView.dequeueReusableCell(withIdentifier: "SecondTVCell", for: indexPath) as! SecondTVCell cell.someOtherStuffLabel.text = stuff[indexPath.row] return cell } }
解决方案
你的问题出在约束设置和header初始化的几个细节上,修正后的代码如下:
import UIKit class SecondVC: UIViewController, UITableViewDelegate, UITableViewDataSource { @IBOutlet weak var secondTableView: UITableView! var stuff = ["Some other stuff 1", "Some other stuff 2", "Some other stuff 3"] var insertionText: String = "" override func viewDidLoad() { super.viewDidLoad() secondTableView.delegate = self secondTableView.dataSource = self // 创建header视图 let tableHeader = UIView() tableHeader.backgroundColor = .systemOrange tableHeader.translatesAutoresizingMaskIntoConstraints = false // 创建两个Label(移除初始frame,完全用AutoLayout控制) let labelOne = UILabel() labelOne.text = "USER:" labelOne.numberOfLines = 0 labelOne.lineBreakMode = .byWordWrapping labelOne.textAlignment = .center labelOne.translatesAutoresizingMaskIntoConstraints = false let labelTwo = UILabel() labelTwo.text = "Inherited text here: \(insertionText)" labelTwo.numberOfLines = 0 labelTwo.lineBreakMode = .byWordWrapping labelTwo.textAlignment = .center labelTwo.translatesAutoresizingMaskIntoConstraints = false // 添加子视图到header tableHeader.addSubview(labelOne) tableHeader.addSubview(labelTwo) // 统一设置约束 NSLayoutConstraint.activate([ // labelOne:顶部、左右留边,高度由内容自动计算 labelOne.topAnchor.constraint(equalTo: tableHeader.topAnchor, constant: 11), labelOne.leadingAnchor.constraint(equalTo: tableHeader.leadingAnchor, constant: 20), labelOne.trailingAnchor.constraint(equalTo: tableHeader.trailingAnchor, constant: -20), // labelTwo:紧跟labelOne底部,左右留边,底部和header留边 labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor, constant: 8), labelTwo.leadingAnchor.constraint(equalTo: tableHeader.leadingAnchor, constant: 20), labelTwo.trailingAnchor.constraint(equalTo: tableHeader.trailingAnchor, constant: -20), labelTwo.bottomAnchor.constraint(equalTo: tableHeader.bottomAnchor, constant: -11) ]) // 先添加约束再计算header尺寸,指定宽度为tableView宽度避免计算错误 let size = tableHeader.systemLayoutSizeFitting( CGSize(width: secondTableView.bounds.width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow ) tableHeader.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height) secondTableView.tableHeaderView = tableHeader } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() guard let tableHeader = secondTableView.tableHeaderView else { return } // 横竖屏切换时重新计算header高度 let size = tableHeader.systemLayoutSizeFitting( CGSize(width: secondTableView.bounds.width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow ) var headerFrame = tableHeader.frame if size.height != headerFrame.height { headerFrame.size.height = size.height tableHeader.frame = headerFrame secondTableView.tableHeaderView = tableHeader } } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { stuff.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = secondTableView.dequeueReusableCell(withIdentifier: "SecondTVCell", for: indexPath) as! SecondTVCell cell.someOtherStuffLabel.text = stuff[indexPath.row] return cell } }
关键修正点
- 移除Label初始frame:完全依赖AutoLayout控制位置,避免布局冲突
- 修正labelTwo的top约束:改用
labelOne.bottomAnchor替代labelOne.layoutMarginsGuide.bottomAnchor,确保两个Label正常衔接 - 调整header尺寸计算时机:先添加子视图和约束,再计算尺寸,避免初始高度为0的问题
- 统一约束锚点:不再混用
layoutMarginsGuide和直接锚点,保持内边距一致 - 增加Label间距:给两个Label之间添加8pt间距,提升视觉效果
内容的提问来源于stack exchange,提问作者CalebGates
相关产品推荐
相关产品推荐

