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