如何在UITableViewCell内根据UITextView高度动态调整单元格尺寸(无需reloadData)
实现UITableViewCell随内部UITextView高度动态调整的方案
我来分享一个亲测有效的方案,完全满足你提出的「不调用reloadData()、保持UITextView第一响应者状态」的要求,步骤拆解如下:
一、先给UITableView搭好自动行高的基础
首先得让TableView支持自动计算行高,这是动态调整的前提:
// 在ViewController的viewDidLoad里配置 tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 // 给个合理的预估高度,优化滚动流畅度
注意estimatedRowHeight不能省略,否则自动行高机制可能不生效。
二、自定义UITableViewCell的布局与监听
我们需要自定义一个包含UITextView的Cell,做好布局约束和内容变化监听:
1. 配置UITextView的基础属性与AutoLayout
要让textView的高度变化直接传递给cell,必须给textView设置和contentView四边绑定的约束,同时禁用textView的内部滚动(不然它只会滚动而不会增高):
class DynamicTextViewCell: UITableViewCell, UITextViewDelegate { let textView = UITextView() // 定义闭包,用来把textView高度变化的事件回调给ViewController var onHeightChange: (() -> Void)? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupTextView() } required init?(coder: NSCoder) { super.init(coder: coder) setupTextView() } private func setupTextView() { textView.delegate = self textView.isScrollEnabled = false // 必须禁用,才能让textView自动增高 textView.font = UIFont.systemFont(ofSize: 16) textView.textContainerInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0) // 可选:调整文本内边距 contentView.addSubview(textView) textView.translatesAutoresizingMaskIntoConstraints = false // 绑定textView到contentView的四边,让textView的高度决定cell的高度 NSLayoutConstraint.activate([ textView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), textView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), textView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), textView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8) ]) } // 监听文本内容变化 func textViewDidChange(_ textView: UITextView) { // 触发回调,通知ViewController更新行高 onHeightChange?() } }
三、ViewController中处理行高更新
在Cell的回调闭包里,我们用beginUpdates()和endUpdates()来触发TableView重新计算当前行的高度——这两个方法不会刷新整个列表,也就不会让textView失去第一响应者状态:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "DynamicTextViewCell", for: indexPath) as! DynamicTextViewCell // 给textView设置初始文本 cell.textView.text = yourDataSource[indexPath.row] // 设置高度变化的回调 cell.onHeightChange = { [weak self] in guard let self = self else { return } // 核心操作:仅重新计算行高,不刷新整个列表 self.tableView.beginUpdates() self.tableView.endUpdates() // 同步更新数据源(根据你的需求可选) self.yourDataSource[indexPath.row] = cell.textView.text } // 如果需要默认让某行的textView成为第一响应者,在这里设置 if indexPath.row == yourTargetRowIndex { cell.textView.becomeFirstResponder() } return cell }
四、关键注意点
- 必须确保
textView.isScrollEnabled = false,否则textView只会内部滚动,不会自动增高。 - 回调闭包一定要用
weak self,避免ViewController和Cell之间的循环引用。 beginUpdates()和endUpdates()是核心,它们会让TableView仅重新计算当前可见行的高度,完全不会触发reloadData(),所以textView的第一响应者状态会稳稳保持。
内容的提问来源于stack exchange,提问作者daniel
相关产品推荐
相关产品推荐

