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

如何设置StackView与TableViewCell内TextView的动态高度?

问题描述

开发待办事项App时遇到TextView高度设置问题,需求如下:

  • TextView基础高度不小于50
  • 高度随内容自动动态调整

已尝试isScrollEnabled = false、.sizeToFit()、translatesAutoresizingMaskIntoConstraints = true等方案,仍未解决,相关代码如下:

class ToDoTableViewCell: UITableViewCell, UITextViewDelegate {
    lazy var backColorView: UIView = {
        let view = UIView()
        view.backgroundColor = .yellow
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    let memoString: UITextView = {
        let textView = UITextView()
        textView.font = .systemFont(ofSize: 17, weight: .regular)
        textView.layer.cornerRadius = 0
        textView.layer.borderWidth = 1
        textView.layer.borderColor = #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)
        textView.backgroundColor = .clear
        textView.isS
        return textView
    }()
    
    let dateString: UILabel = {
        let date = UILabel()
        date.font = .systemFont(ofSize: 14, weight: .light)
        date.text = "2021-11-12"
        return date
    }()
    
    let updateButton: UIButton = {
        let button = UIButton()
        button.setTitle("UPDATE", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 9, weight: .bold)
        button.layer.cornerRadius = 10
        button.backgroundColor = .gray
        button.setImage(UIImage(systemName: "pencil"), for: .normal)
        return button
    }()
    
    let totalStackView: UIStackView = {
        let stack = UIStackView()
        stack.axis = .vertical
        stack.distribution  = .fill
        stack.alignment = .fill
        stack.spacing = 10
        return stack
    }()
    
    let subStackView: UIStackView = {
        let stack = UIStackView()
        stack.axis = .horizontal
        stack.distribution  = .fill
        stack.alignment = .fill
        stack.spacing = 0
        return stack
    }()
    
    
    
    override func awakeFromNib() {
        super.awakeFromNib()
        memoString.delegate = self

    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: .default, reuseIdentifier: reuseIdentifier)
        setupStackView()
        setConstraints()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setupStackView() {
        self.contentView.addSubview(backColorView)
        totalStackView.addArrangedSubview(memoString)
        totalStackView.addArrangedSubview(subStackView)
        subStackView.addArrangedSubview(dateString)
        subStackView.addArrangedSubview(updateButton)
        self.contentView.addSubview(totalStackView)
    }
    
    func setConstraints() {
        totalStackView.translatesAutoresizingMaskIntoConstraints = false
        subStackView.translatesAutoresizingMaskIntoConstraints = false
        updateButton.translatesAutoresizingMaskIntoConstraints = false
        memoString.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            totalStackView.leadingAnchor.constraint(equalTo: backColorView.leadingAnchor, constant: 10),
            totalStackView.trailingAnchor.constraint(equalTo: backColorView.trailingAnchor, constant: -10),
            totalStackView.topAnchor.constraint(equalTo: backColorView.topAnchor, constant: 10),
            totalStackView.bottomAnchor.constraint(equalTo: backColorView.bottomAnchor, constant: -10),
            
            subStackView.heightAnchor.constraint(equalToConstant: 30),
            updateButton.widthAnchor.constraint(equalToConstant: 80),
            
            backColorView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 25),
            backColorView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -25),
            backColorView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
            backColorView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
            
            memoString.heightAnchor.constraint(greaterThanOrEqualToConstant: 50)
        ])
    }
}
class ViewController: UIViewController {
    private let tableView = UITableView()

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        subviews()
        constraints()
        setupTableView()
        makeUI()
        makeButton()
    }

    func setupTableView() {
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.register(ToDoTableViewCell.self, forCellReuseIdentifier: "cell")
    }
}

extension ViewController {
    func makeUI() {
        let navigationBarAppearance = UINavigationBarAppearance()
        navigationBarAppearance.configureWithOpaqueBackground()
        navigationController?.navigationBar.standardAppearance = navigationBarAppearance
        navigationController?.navigationBar.scrollEdgeAppearance = navigationBarAppearance
        navigationController?.navigationBar.tintColor = .blue

        navigationItem.scrollEdgeAppearance = navigationBarAppearance
        navigationItem.standardAppearance = navigationBarAppearance
        navigationItem.compactAppearance = navigationBarAppearance

        navigationController?.setNeedsStatusBarAppearanceUpdate()
        navigationController?.navigationBar.isTranslucent = false
        navigationController?.navigationBar.prefersLargeTitles = true
        title = "메모"
    }
    
    func makeButton() {
        let button = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(action))
        navigationItem.rightBarButtonItem = button
    }
    
    @objc func action() {}
    
    func subviews() {
        view.addSubview(tableView)
    }

    func constraints() {
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
            tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        return cell
    }
}
解决方案

针对需求,调整以下关键设置即可解决问题:

1. 修复TextView初始化代码

初始化时关闭滚动功能(核心设置,否则TextView不会自动调整高度),同时补全语法错误:

let memoString: UITextView = {
    let textView = UITextView()
    textView.font = .systemFont(ofSize: 17, weight: .regular)
    textView.layer.cornerRadius = 0
    textView.layer.borderWidth = 1
    textView.layer.borderColor = #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)
    textView.backgroundColor = .clear
    // 关闭滚动,允许高度自动扩展
    textView.isScrollEnabled = false
    textView.translatesAutoresizingMaskIntoConstraints = false
    return textView
}()

2. 完善TableView自动高度配置

添加预估行高,确保自动维度生效:

func setupTableView() {
    tableView.translatesAutoresizingMaskIntoConstraints = false
    tableView.register(ToDoTableViewCell.self, forCellReuseIdentifier: "cell")
    // 配置自动行高与预估高度
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 100
}

3. 内容变化时刷新行高

实现TextView代理方法,当内容修改时通知TableView更新当前行高度:
在ToDoTableViewCell中添加:

// 添加弱引用获取所在TableView
weak var tableView: UITableView?

func textViewDidChange(_ textView: UITextView) {
    // 触发行高更新
    tableView?.beginUpdates()
    tableView?.endUpdates()
}

然后在ViewController的cellForRowAt方法中绑定引用:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ToDoTableViewCell
    cell.tableView = tableView
    // 可设置测试文本验证效果
    cell.memoString.text = "测试文本\n多行内容\n验证高度是否自动调整"
    return cell
}

4. 保留现有约束

你已设置memoString.heightAnchor.constraint(greaterThanOrEqualToConstant: 50),该约束满足“基础高度不小于50”的需求,无需修改。

完成以上修改后,TextView会在内容较少时保持50高度,内容增多时自动扩展,同时TableView行高也会同步调整。

内容的提问来源于stack exchange,提问作者피우리

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:44:56