Để UITableViewCell tự điều chỉnh chiều cao theo nội dung (dynamic height), dùng UITableView.automaticDimension kết hợp estimatedRowHeight.
Setup TableView
func setupTableView() {
let tableView = UITableView(frame: view.bounds, style: .plain)
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.backgroundColor = .systemGroupedBackground
// KEY: Bật automatic dimension
tableView.estimatedRowHeight = 100 // Ước lượng height (giúp scroll indicator mượt)
tableView.rowHeight = UITableView.automaticDimension
view.addSubview(tableView)
}
DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 5 }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
// QUAN TRỌNG: Cell mặc định có textLabel.numberOfLines = 1
// Phải set = 0 để multiline
cell.textLabel?.numberOfLines = 0
cell.textLabel?.font = .systemFont(ofSize: 16)
// Nội dung khác nhau cho từng row
if indexPath.row == 1 {
cell.textLabel?.text = "Dòng ngắn"
} else if indexPath.row == 2 {
cell.textLabel?.text = "Dòng rất dài sẽ tự động xuống hàng nhiều lần để hiển thị toàn bộ nội dung này trong cell mà không bị cắt ngắn hay truncate."
} else {
cell.textLabel?.text = "Row \(indexPath.row)"
}
return cell
}
KHÔNG implement heightForRowAt
Khi dùng automaticDimension, KHÔNG implement heightForRowAt (hoặc return UITableView.automaticDimension).
Kết quả
Row 2 (dòng dài) tự cao hơn các row khác:

Yêu cầu bắt buộc cho Auto Layout trong Cell
- Cell content view có constraints từ top đến bottom (vertical chain).
- Nếu dùng custom cell xib/storyboard: Các subview phải pinned top/bottom tới contentView.
- Label:
numberOfLines = 0, có constraint top/bottom/leading/trailing. - ImageView: Cần aspect ratio constraint hoặc fixed height.
Custom Cell với Auto Layout (Xib/Storyboard)
class CustomCell: UITableViewCell {
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var bodyLabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
titleLabel.numberOfLines = 0
bodyLabel.numberOfLines = 0
}
}
Lưu ý
estimatedRowHeightgiúp scroll bar mượt – set giá trị trung bình mong đợi.- iOS 11+:
rowHeight = UITableView.automaticDimensionlà default, nhưng set explicit cho rõ ràng. - Performance: Automatic dimension tính toán layout mỗi cell – với hàng nghìn row phức tạp, cân nhắc cache height hoặc fixed height.
- Self-sizing cells cũng áp dụng cho
UICollectionView(estimatedItemSize).