Dynamic height UITableView
Lưu ý: Bài viết này không nói về cách tự động resize UITableViewCell
mà là về cách khiến bản thân UITableView
tự động thay đổi chiều cao dựa theo nội dung của nó.
Trong quá trình làm việc mình thường xuyên gặp trường hợp mà việc UITableView
tự động resize sẽ khiến công việc nhẹ nhàng hơn rất nhiều, đặc biệt khi bạn muốn show một danh sách các lựa chọn như hình dưới đây.

Hai cách thông dụng để xử lí trường hợp trên là tự thay frame của UITableView
hoặc kéo height constraint của UITableView
rồi set constant bằng code. Cả hai cách làm trên đều thủ công và rắc rối hơn mức cần thiết.
Tự động resize UITableView
Biến contentSize
của UITableView
là size chính xác của chiều rộng UITableView
và tổng chiều cao của nội dung bên trong nó. contentSize
được hệ thống tính toán một cách tự động mỗi khi data của UITableView
thay đổi nên bạn có thể yên tâm về độ chính xác và bỏ qua các bước tính toán thủ công.
Tuy nhiên contentSize
thôi là chưa đủ vì nó chỉ đại diện cho chiều cao của nội dung trong UITableView
chứ không đại diện cho chiều cao của UITableView
. Để đồng bộ giá trị này với chiều cao thực tế của UITableView
, ta cần sử dụng intrinsicContentSize
.
intrinsicContentSize
Một vài UIView
có size tự nhiên dựa theo nội dung của nó. Các ví dụ có thể kể đến như UIButton
, UILabel
(với numberOfLines
khác 0), hoặc bất kì view nào trong file giao diện mà không bị báo lỗi khi thiếu constraint width và height. Size tự nhiên này chính là intrinsicContentSize
. Đối với những view không có size tự nhiên như UITableView
, giá trị này sẽ bằng (-1, -1).
Với hiểu biết về intrinsicContentSize
ta đã có thể giúp UITableView
tự động thay đổi chiều cao mà không cần set frame hay set height constraint cho nó. Tất cả những gì chúng ta phải làm là subclass SelfSizingTableView
và cập nhật intrinsicContentSize
mỗi khi contentSize
của UITableView
thay đổi.
class SelfSizingTableView: UITableView {
var maxHeight: CGFloat = .infinity
override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
setNeedsLayout()
}
}
override var intrinsicContentSize: CGSize {
let height = min(maxHeight, contentSize.height)
return CGSize(width: contentSize.width, height: height)
}
}
Bất kì khi nào contentSize
được set, hàm invalidateIntrinsicContentSize
sẽ được gọi để huỷ intrinsicContentSize
cũ. Ngay sau đó ta khiến hệ thống cập nhật lại layout mới nhất bằng cách gọi setNeedsLayout
. Trong trường hợp cần giới hạn chiều cao của UITableView
bạn có thể set lại giá trị maxHeight
. Sau khi đạt đến giá trị tối đa cho phép, UITableView
sẽ scroll như bình thường.
Dùng SelfSizingTableView trong file giao diện
Để UITableView
tự động resize, bạn không được đặt constraint chiều cao hay fix cứng top của SelfSizingTableView
. Tuy nhiên việc setup không đủ constraint này sẽ bị Xcode báo lỗi.

Bình thường UITableView
sẽ không được hiển thị chính xác nếu thiếu constraint nhưng trong trường hợp này ta biết rằng UITableView
chắc chắn sẽ có chiều cao - intrinsicContentSize
khác (-1, -1) nên ta có thể tắt warning bằng cách đổi Intrinsic Size trong file giao diện từ Default sang Placeholder.

SelfSizingTableView
hoạt động tốt với cả cell có chiều cao cố định lẫn dynamic nên bạn có thể yên tâm sử dụng. Nếu có bất kì thắc mắc hay góp ý gì đừng ngại gửi mail cho mình nhé.