Swift:不使用自动尺寸计算TableView中动态TextView的行高
嘿,太懂你这种情况了——自动行高在嵌套ContainerView的场景里确实容易出滚动冲突的幺蛾子!你现在实例化TextView算高度的思路是对的,但可以优化得更高效,给你两个靠谱的方案:
方案1:用
NSString直接计算高度(最轻量无依赖) 这个方法不用创建任何UI组件,直接通过文本属性和指定宽度算出高度,性能拉满。关键是要和你Cell里的TextView属性完全对齐:
func calculateTextViewHeight(for text: String, targetWidth: CGFloat) -> CGFloat { // 和Cell内TextView一致的字体 let font = UIFont.preferredFont(forTextStyle: .body) // 如果有自定义行间距,记得加上这个属性 let textAttributes: [NSAttributedString.Key: Any] = [ .font: font, .lineSpacing: 4 // 假设你的TextView设置了4px行间距,没有就去掉 ] // 计算文本占据的矩形 let boundingRect = text.boundingRect( with: CGSize(width: targetWidth, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: textAttributes, context: nil ) // 加上TextView的默认内边距(和Cell里的保持一致,比如上下各8px) let textViewInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) // 取整避免小数高度导致的布局模糊 return ceil(boundingRect.height) + textViewInsets.top + textViewInsets.bottom }
这里的targetWidth是TextView在Cell里的实际宽度:比如TableView宽度减去Cell的左右间距、TextView的左右约束值,别直接用TableView宽度哦!
方案2:复用全局TextView(和你的思路一致但更高效)
你现在每次实例化新TextView的做法有点浪费资源,可以在ViewController里初始化一个全局的“计算专用TextView”,只创建一次,每次计算时更新文本和宽度就行:
// 全局存储,仅初始化一次 private let calculationTextView = UITextView() override func viewDidLoad() { super.viewDidLoad() // 提前配置和Cell内TextView完全相同的属性 calculationTextView.font = UIFont.preferredFont(forTextStyle: .body) calculationTextView.textContainerInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) calculationTextView.textContainer.lineFragmentPadding = 0 // 去掉默认左右内边距(如果你的Cell里也这么设置了) } func calculateTextViewHeight(for text: String, targetWidth: CGFloat) -> CGFloat { calculationTextView.text = text calculationTextView.frame = CGRect(x: 0, y: 0, width: targetWidth, height: CGFloat.greatestFiniteMagnitude) // 强制更新布局,确保contentSize准确 calculationTextView.layoutIfNeeded() // 取整返回高度 return ceil(calculationTextView.contentSize.height) }
这个方案的好处是完全和Cell里的TextView行为一致,不用手动处理行间距、内边距这些细节,适合属性复杂的TextView。
在
heightForRowAt里的用法 不管用哪个方案,直接在代理方法里调用计算逻辑就行:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { let cellText = yourDataSource[indexPath.row] // 计算TextView的实际宽度:TableView宽度 - 左右间距(假设各16px) let textViewWidth = tableView.bounds.width - 16 * 2 let textViewHeight = calculateTextViewHeight(for: cellText, targetWidth: textViewWidth) // 加上Cell内其他元素的高度和间距(比如标题、上下边距) return textViewHeight + 20 // 假设上下各10px的额外间距 }
几个关键注意点
- 所有计算用的属性(字体、内边距、行间距、换行模式)必须和Cell里的TextView完全一致,不然高度会偏差;
- 一定要用
ceil取整,iOS对小数高度的布局处理容易出现模糊或错位; - 如果TextView有富文本,方案1里的
textAttributes要包含所有富文本属性,方案2则直接设置attributedText即可。
内容的提问来源于stack exchange,提问作者alionthego
相关产品推荐
相关产品推荐

