如何根据attributedText自动调整tableView的tableHeaderView高度?
解决TableView HeaderView 富文本动态高度计算偏小的问题
嘿,这个问题我之前做项目的时候也碰到过!手动计算富文本高度确实容易踩各种小坑导致高度偏小,咱们一步步来解决它~
先搞清楚为什么高度会偏小
手动计算高度时,以下几个点最容易出错:
- 没有匹配实际的显示容器宽度:计算时用的宽度如果比label实际能显示的宽度宽,算出来的高度就会偏小(比如直接用tableView的宽度,但label左右有内边距)
- 忽略了文本的附加属性:富文本的
lineSpacing、paragraphStyle这些属性如果在计算时没同步设置,结果会和实际显示高度不符 - 计算方法的参数没传对:用
boundingRect(with:options:context:)时,必须传入.usesLineFragmentOrigin选项,否则文本不会按多行布局计算高度 - 没考虑HeaderView或Label的内边距:如果header本身有padding,或者label设置了
contentInset,计算出的文本高度还要加上这些额外的间距
修正后的手动计算高度代码
你可以试试用下面的方式来计算,确保和实际显示的条件完全匹配:
func setupTableHeader() { let str = "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium dol..." // 1. 构建和实际显示一致的富文本属性 let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 8 // 和你label里设置的一致 paragraphStyle.alignment = .left // 匹配label的对齐方式 let attributedText = NSAttributedString(string: str, attributes: [ .font: UIFont.systemFont(ofSize: 16), // 和label的font一致 .paragraphStyle: paragraphStyle ]) // 2. 确定label的实际可用宽度(tableView宽度 - 左右内边距 - label的左右约束间距) let tableViewWidth = self.tableView.bounds.width let labelAvailableWidth = tableViewWidth - 20 - 20 // 假设左右各20的内边距 // 3. 计算文本高度 let textSize = attributedText.boundingRect( with: CGSize(width: labelAvailableWidth, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil ).size // 4. 计算header的总高度(文本高度 + 上下内边距) let headerHeight = textSize.height + 16 + 16 // 假设上下各16的内边距 // 5. 创建并设置headerView let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableViewWidth, height: headerHeight)) headerView.backgroundColor = .white let label = UILabel() label.frame = CGRect(x: 20, y: 16, width: labelAvailableWidth, height: textSize.height) label.attributedText = attributedText label.numberOfLines = 0 headerView.addSubview(label) self.tableView.tableHeaderView = headerView }
更推荐的方案:用AutoLayout实现动态高度(不用手动计算)
其实HeaderView完全可以像Cell一样用AutoLayout来实现自动高度,这样更可靠,也不用手动算来算去:
步骤1:创建自定义HeaderView
class CustomTableHeaderView: UIView { let contentLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 label.translatesAutoresizingMaskIntoConstraints = false return label }() override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } private func setupUI() { addSubview(contentLabel) // 设置约束:label和header的上下左右都有间距 NSLayoutConstraint.activate([ contentLabel.topAnchor.constraint(equalTo: topAnchor, constant: 16), contentLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20), contentLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20), contentLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16) ]) backgroundColor = .white } }
步骤2:在TableView中使用这个HeaderView
func setupTableHeader() { let str = "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium dol..." // 构建富文本 let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineSpacing = 8 let attributedText = NSAttributedString(string: str, attributes: [ .font: UIFont.systemFont(ofSize: 16), .paragraphStyle: paragraphStyle ]) let headerView = CustomTableHeaderView() headerView.contentLabel.attributedText = attributedText // 让header根据内容计算合适的尺寸 headerView.layoutIfNeeded() let headerSize = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) headerView.frame = CGRect(x: 0, y: 0, width: tableView.bounds.width, height: headerSize.height) tableView.tableHeaderView = headerView }
这种方式完全依赖AutoLayout来计算高度,和Cell的动态高度逻辑一致,几乎不会出现高度计算偏差的问题,我现在做项目都优先用这种方法~
内容的提问来源于stack exchange,提问作者JasonAddFour
相关产品推荐
相关产品推荐

