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

如何实现TableView单元格高度动态切换:点击按钮收起TextView并缩小单元格

嘿Alex,刚好我之前做过类似的需求,给你详细讲讲怎么实现这个支持折叠/展开、两种高度的TableView单元格,步骤和代码都给你整理好了👇

实现TableView单元格的折叠/展开(双高度切换)

核心思路是通过维护单元格的展开状态,结合自动布局让TableView自动计算不同状态下的单元格高度,点击按钮时切换状态并刷新对应单元格即可。

第一步:定义带状态的数据源模型

首先要给每个单元格对应的数据加一个标记,记录当前是「展开」还是「收起」状态,这是控制高度变化的核心:

struct CellData {
    let title: String          // Label显示的标题
    let content: String        // TextView显示的内容
    var isExpanded: Bool       // 标记单元格是否展开
}

(如果用Objective-C,思路完全一致,只需把结构体改成带BOOL isExpanded属性的OC类即可)

第二步:自定义可折叠的TableViewCell

创建自定义Cell,包含Label、TextView和Button三个组件,用自动布局做好约束,同时给按钮添加点击回调,用来通知控制器切换状态:

class ExpandableCell: UITableViewCell {
    // 初始化组件
    private let titleLabel: UILabel = {
        let label = UILabel()
        label.font = .systemFont(ofSize: 16, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let contentTextView: UITextView = {
        let tv = UITextView()
        tv.font = .systemFont(ofSize: 14)
        tv.isEditable = false
        tv.isScrollEnabled = false // 收起时不需要滚动
        tv.translatesAutoresizingMaskIntoConstraints = false
        return tv
    }()
    
    private let toggleBtn: UIButton = {
        let btn = UIButton(type: .system)
        btn.setTitle("收起", for: .normal)
        btn.setTitle("展开", for: .selected)
        btn.translatesAutoresizingMaskIntoConstraints = false
        return btn
    }()
    
    // 按钮点击的回调闭包,用来通知控制器切换状态
    var toggleHandler: (() -> Void)?
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupLayout()
        setupBtnAction()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // 配置组件布局约束
    private func setupLayout() {
        contentView.addSubview(titleLabel)
        contentView.addSubview(contentTextView)
        contentView.addSubview(toggleBtn)
        
        NSLayoutConstraint.activate([
            titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 16),
            titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            titleLabel.trailingAnchor.constraint(equalTo: toggleBtn.leadingAnchor, constant: -8),
            
            toggleBtn.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
            toggleBtn.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            
            contentTextView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 12),
            contentTextView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            contentTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16)
        ])
    }
    
    // 绑定按钮点击事件
    private func setupBtnAction() {
        toggleBtn.addTarget(self, action: #selector(toggleCellState), for: .touchUpInside)
    }
    
    @objc private func toggleCellState() {
        toggleHandler?()
    }
    
    // 给Cell配置数据,同时根据状态更新UI
    func configure(with data: CellData) {
        titleLabel.text = data.title
        contentTextView.text = data.content
        // 收起时隐藏TextView,展开时显示
        contentTextView.isHidden = !data.isExpanded
        // 切换按钮的选中状态,对应显示"展开"/"收起"文字
        toggleBtn.isSelected = !data.isExpanded
    }
}

第三步:控制器层的逻辑处理

在ViewController里维护数据源,处理按钮点击后的状态切换,同时让TableView自动计算不同状态下的单元格高度:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    private let tableView = UITableView()
    // 初始化测试数据源
    private var cellDataList: [CellData] = [
        CellData(title: "标题1", content: "这里是一段较长的测试内容,展开后会完整显示,收起时TextView会隐藏,单元格高度自动缩小。", isExpanded: true),
        CellData(title: "标题2", content: "这是另一段测试内容,用来验证折叠展开的动画效果是否流畅自然。", isExpanded: false)
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupTableView()
    }
    
    private func setupTableView() {
        tableView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(tableView)
        
        // 布局TableView
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
        
        // 注册自定义Cell
        tableView.register(ExpandableCell.self, forCellReuseIdentifier: "ExpandableCell")
        tableView.dataSource = self
        tableView.delegate = self
        // 设置自动计算单元格高度
        tableView.rowHeight = UITableView.automaticDimension
        // 设置预估高度,提升TableView滚动性能
        tableView.estimatedRowHeight = 80
    }
    
    // MARK: - UITableView数据源方法
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return cellDataList.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell", for: indexPath) as! ExpandableCell
        let data = cellDataList[indexPath.row]
        cell.configure(with: data)
        
        // 绑定按钮点击的回调:切换状态并刷新当前单元格
        cell.toggleHandler = { [weak self] in
            guard let self = self else { return }
            self.cellDataList[indexPath.row].isExpanded.toggle()
            // 只刷新当前单元格,带动画效果
            tableView.reloadRows(at: [indexPath], with: .automatic)
        }
        
        return cell
    }
}

关键注意点

  • 自动布局是核心:确保Cell内所有组件都有完整的约束,这样UITableView.automaticDimension才能正确计算不同状态下的高度。
  • 精准刷新单元格:用reloadRows(at:with:)代替reloadData(),既能保证动画流畅,又不会影响其他单元格的状态。
  • 可选优化:如果不想完全隐藏TextView,也可以通过设置textView.numberOfLines来实现「收起时显示N行,展开时显示全部」的效果,只需修改Cell的configure方法即可。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 03:36:46