I am having a hard time making my implementation of UITableView with automatic cell sizing. I am trying to use a UIStackView with 2 labels inside cell with automatic size.
It looks good overall but I am getting the following runtime issues:
I feel like the constraints I have should be enough for the use case here but I would like to get rid of the ambiguity here.
How that can be achieved? What is the common approach to take here?
I have prepared a sample project that simplifies my use case but still demonstrates the issue. Here is my controller:
class ViewController: UIViewController {
struct CellData {
let title: String
let subtitle: String
}
let table = UITableView()
let data = [
CellData(title: "Foo", subtitle: "Bar"),
CellData(title: "Baz", subtitle: "FooBar")
]
private let cellReuseID = "CellReuseID"
override func viewDidLoad() {
super.viewDidLoad()
table.register(Cell.self, forCellReuseIdentifier: cellReuseID)
table.dataSource = self
table.rowHeight = UITableView.automaticDimension
table.tableFooterView = UIView(frame: .zero)
table.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(table)
NSLayoutConstraint.activate([
table.leadingAnchor.constraint(equalTo: view.leadingAnchor),
table.trailingAnchor.constraint(equalTo: view.trailingAnchor),
table.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
table.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseID) as? Cell else {
fatalError()
}
cell.title = data[indexPath.row].title
cell.subtitle = data[indexPath.row].subtitle
return cell
}
}
And the cell it uses:
class Cell: UITableViewCell {
var title: String? {
didSet {
titleLabel.text = title
}
}
var subtitle: String? {
didSet {
subtitleLabel.text = subtitle
}
}
let titleLabel = UILabel()
let subtitleLabel = UILabel()
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() {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.alignment = .top
stackView.distribution = .fill
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
stackView.topAnchor.constraint(equalTo: contentView.topAnchor)
])
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(subtitleLabel)
}
}
CodePudding user response:
The reason for the Ambiguous Height is because both labels have the same Content Hugging Priority.
Auto-layout makes its "best guess" and you get the desired output -- but you'll still see the issues in Debug View Hierarchy.
To get rid of it, you can give one label a higher Hugging Priority.
For example (in setupView()
):
titleLabel.setContentHuggingPriority(.defaultHigh 1, for: .vertical)
subtitleLabel.setContentHuggingPriority(.defaultHigh, for: .vertical)