You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:16:24