如何根据文本内容动态设置UITableViewCell中UITextView的高度?
解决UITableViewCell中动态高度UITextView的行高适配问题
你当前的问题主要出在行高计算逻辑错误和Auto Layout约束配置不当这两个点上,咱们一步步来解决:
一、先理清你现有代码的问题
你在heightForRowAt里调用tableView.cellForRow(at: indexPath)是完全错误的——因为系统计算行高的时机,早于单元格的创建/复用时机,这个方法此时返回的cell大概率是nil,所以你的if分支根本不会执行,自然所有行都返回固定的120高度。另外messageView不显示,和你没给它配置完整的垂直约束直接相关。
二、推荐方案:用Auto Layout自动计算行高(最可靠)
这是苹果官方推荐的方式,不需要手动计算行高,只需要做好约束配置和TableView设置:
1. 正确配置messageView的约束
确保你的messageView满足以下条件:
- 不要给它设置固定高度约束
- 配置完整的垂直约束链:比如顶部和cell内的其他元素(或contentView顶部)相连,底部和contentView底部相连,同时设置左右边距
- 必须设置
messageView.isScrollEnabled = false(禁止滚动,TextView才会自动根据文本换行并调整自身高度)
示例PostCell的约束配置代码:
class PostCell: UITableViewCell { let messageView: UITextView = { let tv = UITextView() tv.translatesAutoresizingMaskIntoConstraints = false tv.font = UIFont.systemFont(ofSize: 17) // 和你实际使用的字体一致 tv.isScrollEnabled = false // 关键!必须关闭滚动 tv.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) // 可选,调整文本内边距 return tv }() // 假设你cell里其他元素的高度总和是120,这里用一个占位view模拟 let topContainerView: UIView = { let view = UIView() view.translatesAutoresizingMaskIntoConstraints = false view.backgroundColor = .systemBlue return view }() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupSubviews() { contentView.addSubview(topContainerView) contentView.addSubview(messageView) NSLayoutConstraint.activate([ // 顶部容器固定高度120,对应你的最小高度基础 topContainerView.topAnchor.constraint(equalTo: contentView.topAnchor), topContainerView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), topContainerView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), topContainerView.heightAnchor.constraint(equalToConstant: 120), // messageView的约束:顶部接顶部容器,底部接contentView底部,左右留边 messageView.topAnchor.constraint(equalTo: topContainerView.bottomAnchor, constant: 12), messageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), messageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), messageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12) ]) } }
2. 配置TableView的自动行高
在你的ViewController的viewDidLoad里添加以下代码:
override func viewDidLoad() { super.viewDidLoad() tableView.register(PostCell.self, forCellReuseIdentifier: "PostCell") // 开启自动行高 tableView.rowHeight = UITableView.automaticDimension // 设置一个接近最小行高的估计值,帮助系统优化滚动性能 tableView.estimatedRowHeight = 140 // 120+24(messageView的上下边距) }
3. 简化cellForRowAt实现
不需要额外处理高度,只需要给messageView赋值文本即可:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexPath) as! PostCell let message = yourDataSourceArray[indexPath.row] // 替换成你的数据源 cell.messageView.text = message return cell }
三、如果一定要手动计算行高(不推荐,但满足你的需求)
如果你坚持要手动计算120 + messageView高度,那应该直接用数据源文本计算,而不是去获取cell:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let baseHeight: CGFloat = 120 let message = yourDataSourceArray[indexPath.row] // 计算文本需要的高度:要和messageView的字体、宽度一致 let textWidth = tableView.bounds.width - 32 // 对应messageView的左右边距16*2 let textAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.systemFont(ofSize: 17) ] let textSize = message.boundingRect( with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil ).size // 加上messageView的上下边距(如果有) return baseHeight + textSize.height + 24 // 24是示例中messageView的上下边距12*2 }
关键总结
- 永远不要在
heightForRowAt里调用cellForRow(at:),时机不对会导致拿不到有效cell - Auto Layout自动行高是最稳定的方案,核心是保证
messageView的约束完整且无固定高度,同时关闭滚动 - 手动计算行高时,必须基于数据源文本和TextView的实际配置(字体、宽度)来计算
内容的提问来源于stack exchange,提问作者K.Wu
相关产品推荐
相关产品推荐

