Home > OS >  How to dynamically resize UILabel in a UITableView Cell?
How to dynamically resize UILabel in a UITableView Cell?

Time:06-16

I defined a CustomCell type in my UITableView (shown below), which contains two labels in a stackView. The second label in the stackView has its numberOfLines set to 2. However, when this line has 1 line of data, it causes the second label to be cut off vertically (shown below).

How can I update the constraints so that the second label can dynamically adjust based on if there are 1 or 2 lines of data?

What I tried:

  • Setting the stackView's distribution to fillProportionally, this only works if there are 2 lines of data in the second label
  • Adding spacing to the stackView to separate the two labels
  • Removing the stackView, and constraining the two labels individually

enter image description here

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)
       ])
   }
}

CodePudding user response:

class CustomCell: UITableViewCell {
    private let typeLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 25, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let nameLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.systemFont(ofSize: 20)
        label.numberOfLines = 2
        label.lineBreakMode = .byWordWrapping
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let iv: UIImageView = {
        let iv = UIImageView()
        iv.contentMode = .scaleAspectFit
        iv.translatesAutoresizingMaskIntoConstraints = false
        iv.backgroundColor = .systemMint
        return iv
    }()
    
    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")
    }

    private func setupView() {
        contentView.backgroundColor = .systemPurple
        
        contentView.addSubview(typeLabel)
        typeLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10).isActive = true
        typeLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
        typeLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, constant: -20).isActive = true
        
        contentView.addSubview(nameLabel)
        nameLabel.topAnchor.constraint(equalTo: typeLabel.bottomAnchor, constant: 5).isActive = true
        nameLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
        nameLabel.widthAnchor.constraint(equalTo: contentView.widthAnchor, constant: -20).isActive = true
        
        contentView.addSubview(iv)
        iv.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 10).isActive = true
        iv.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10).isActive = true
        iv.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.55).isActive = true
        iv.heightAnchor.constraint(equalTo: iv.widthAnchor, multiplier: 0.6).isActive = true
        iv.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10).isActive = true
    }
    
    func configure(with mealInfo: Data) {
        typeLabel.text = mealInfo.type.capitalized
        nameLabel.text = mealInfo.name
        iv.image = UIImage(systemName: "star")
    }
 }
  • Related