Reputation: 386
I'm having a hard time setting my tableView
height according to it's content. My cells have dynamic heights and if I hard code an height for my table view I'm able to see that they are fine. Now my problem is:
My table view doesn't resize at the correct height according to my cells
If I do not set a static height for the table view, the cells are not rendered
BONUS ISSUE: I use SnapKit to make constraints and when I remake constraints to fit my table view height according to content size, it fires viewDidLoad
as many times as I have cells*
All things being said here is some code:
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
private var cellHeight: CGFloat? = UITableView.automaticDimension
private var hasLoader: Bool = true
lazy var tableView: UITableView! = UITableView()
lazy var loadMoreButton: UIButton! = PrimaryButton(textKey: "actionSeeMore")
lazy var emptyView: UILabel! = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(Cell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.delegate = self
tableView.separatorStyle = .none
tableView.rowHeight = UITableView.automaticDimension
initActions()
initLayout()
}
override func viewWillAppear(_ animated: Bool) {
loadList(appendItems: true)
}
func initLayout() {
self.view.addSubview(tableView)
self.view.addSubview(emptyView)
self.view.addSubview(loadMoreButton)
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.snp.makeConstraints { (make) -> Void in
make.left.right.leading.trailing.top.equalToSuperview()
if(self.hasLoader == true) {
make.bottom.equalTo(self.loadMoreButton.snp.top).offset(-15)
}
print("PRINT TABLE HEIGHT", self.tableView.contentSize.height) // It's 0.0
make.height.equalTo(1000) // Just trying things
}
tableView.isScrollEnabled = false
emptyView.translatesAutoresizingMaskIntoConstraints = false
emptyView.isHidden = true
emptyView.textAlignment = .center
emptyView.snp.makeConstraints { (make) -> Void in
make.left.right.leading.trailing.equalToSuperview()
make.top.equalToSuperview().offset(8)
}
self.loadMoreButton.isHidden = true
if(self.hasLoader == true) {
self.loadMoreButton.isHidden = false
loadMoreButton.translatesAutoresizingMaskIntoConstraints = false
loadMoreButton.snp.makeConstraints { (make) -> Void in
make.centerX.equalToSuperview()
make.top.equalTo(tableView.snp.bottom)
}
}
}
func initActions() {
loadMoreButton.addTarget(self, action: #selector(self.loadMoreOnClick(sender:)), for: .touchUpInside)
}
func loadList(appendItems: Bool) {
DispatchQueue.main.async {
self.apiClient.getList(completion: { _ in
DispatchQueue.main.async {
self.tableView.reloadData()
// Here I normally do tableView.snp.remakeConstraints { (make) in make.height.equalTo(self.tableView.contentSize.height) } which fires viewDidLoad many times (as much as I have cells)
}
})
}
}
func loadMore() {
self.page += 1
self.loadList(appendItems: true)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return resourceList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! Cell
let currentResource = resourceList[indexPath.row]
cell.setResource(resource: currentResource)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.tableView.deselectRow(at: indexPath, animated: true)
let resource = resourceList[indexPath.row]
if (resource.getRouteParam() != "") {
router.setRoute(routeName: resource.getRouteName(), routeParam:
resource.getRouteParam())
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
I have omitted parts of the code for readability.
Upvotes: 1
Views: 2470
Reputation: 10102
Try following :
UIViewController
subclass like this.private var contentSizeObservation: NSKeyValueObservation?
viewDidLoad()
or whenever your myTableView
is set up, start observing contentSize changes.contentSizeObservation = myTableView.observe(\.contentSize, options: .new, changeHandler: { [weak self] (tv, _) in
guard let self = self else { return }
self.myTableViewHeightConstraint.constant = tv.contentSize.height
})
deinit
call -deinit {
contentSizeObservation?.invalidate()
}
This approach will make sure that your myTableView
is always as big in height as it's content.
Upvotes: 8