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

如何动态调整UITableViewCell内UILabel高度适配内容行数

问题说明

在UITableView的自定义Cell中,垂直StackView内numberOfLines = 2的UILabel在仅显示1行文本时出现纵向截断,无法根据实际文本行数动态调整高度。已尝试调整StackView分布模式、添加间距、移除StackView单独约束均未完全解决问题。
标签截断效果截图
附原有实现代码:

struct Data {
    var type, name: String
}

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    var data = [Data]()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupView()
    }

    lazy var tableView: UITableView = {
        let tableView = UITableView()
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(CustomCell.self, forCellReuseIdentifier: "cell")
        
        tableView.backgroundColor = .white
        tableView.separatorStyle = .none
        
        return tableView
    }()
    
    func setupView() {
        data = [
            Data(type: "Breakfast", name: "Poached Egg & Avocado Toast with Sliced Cherry Tomatoes"),
            Data(type: "Lunch", name: "Poached Egg & Avocado Toast")
        ]
        
        view.backgroundColor = .white
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            tableView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9),
            tableView.topAnchor.constraint(equalTo: view.topAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! CustomCell
        let data = data[indexPath.row]
        
        cell.configure(with: data)
        cell.backgroundColor = .white

        return cell
    }
}
                 
class CustomCell: UITableViewCell {
   override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
       super.init(style: style, reuseIdentifier: reuseIdentifier)
       setupView()
   }
   
   required init?(coder: NSCoder) {
       fatalError("init(coder:) has not been implemented")
   }
   
   override func layoutSubviews() {
       super.layoutSubviews()
       contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0))
   }
   
   lazy var typeLabel: UILabel = {
       let label = UILabel()
       label.font = UIFont.systemFont(ofSize: 25, weight: .bold)
       label.translatesAutoresizingMaskIntoConstraints = false
       
       return label
   }()
   
   lazy var nameLabel: UILabel = {
       let label = UILabel()
       label.font = UIFont.systemFont(ofSize: 20)
       label.numberOfLines = 2
       label.lineBreakMode = .byWordWrapping
       label.translatesAutoresizingMaskIntoConstraints = false
       
       return label
   }()
   
   lazy var titleStack: UIStackView = {
       let stack = UIStackView(arrangedSubviews: [typeLabel, nameLabel])
       stack.axis = .vertical
       stack.distribution = .fillProportionally
       stack.translatesAutoresizingMaskIntoConstraints = false

       return stack
   }()
   
   lazy var iv: UIImageView = {
       let iv = UIImageView()
       iv.contentMode = .scaleAspectFit
       iv.translatesAutoresizingMaskIntoConstraints = false
       iv.backgroundColor = .systemMint
       
       return iv
   }()
   
   func configure(with mealInfo: Data) {
       typeLabel.text = mealInfo.type.capitalized
       nameLabel.text = mealInfo.name
       iv.image = UIImage(systemName: "star")
   }

   func setupView() {
       contentView.backgroundColor = .systemPurple
       contentView.addSubview(titleStack)
       contentView.addSubview(iv)

       NSLayoutConstraint.activate([
           titleStack.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
           titleStack.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor, constant: 10),
           titleStack.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.9)
       ])
       
       NSLayoutConstraint.activate([
           iv.topAnchor.constraint(equalTo: titleStack.bottomAnchor, constant: 10),
           iv.leftAnchor.constraint(equalTo: titleStack.leftAnchor),
           iv.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.55),
           iv.heightAnchor.constraint(equalTo: iv.widthAnchor, multiplier: 0.6),
           iv.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10)
       ])
   }
}
问题根因

显示异常来自三个配置错误:

  1. 垂直StackView使用了fillProportionally分布模式,该模式会按照子视图的固有尺寸比例强制分配高度,当nameLabel仅1行文本时,固有高度被比例规则压缩,导致文本截断
  2. 未开启UITableView自动行高能力,AutoLayout无法根据内容动态计算Cell高度
  3. 在layoutSubviews中手动修改contentView的frame,破坏了AutoLayout的尺寸计算链路
解决方案

按以下步骤修改配置即可实现标签高度动态适配:

  • 修正StackView配置:将titleStack的distribution属性从fillProportionally改为.fill,该模式会自动尊重每个子视图的固有内容高度,不会强行压缩单行文本的标签。可按需设置stack.spacing = 8控制两个标签的垂直间距,不需要给两个Label额外添加高度约束,设置了numberOfLines = 2的UILabel会自动根据文本内容返回正确的固有高度。
  • 开启TableView自动行高:在tableView初始化闭包中添加两行配置,让系统自动根据约束计算行高:
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 150 // 填入接近实际行高的估算值即可
    
  • 移除破坏布局的代码:删除layoutSubviews中手动修改contentView frame的逻辑,需要Cell底部间距时,直接调整iv的bottom约束常量即可,比如将原约束的constant: -10改为constant: -30即可实现和原有逻辑一致的底部边距,不会干扰AutoLayout计算。

修改完成后,垂直方向约束链完全闭合(StackView顶部接contentView顶部 -> StackView底部接图片顶部 -> 图片底部接contentView底部),系统会自动根据nameLabel的实际文本行数(1行/2行)计算整个Cell的正确高度,不会再出现文本截断问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:36:18