I would like to recalculate the height of a table view's footer based upon the table view's changing content size. When the table has zero rows the height of the footer will be at its maximum. As rows are added to the table the footer's height will be reduced until it reaches a minimum. What I am doing is using the footer to fill up the empty space that appears at the bottom of the table when there are zero or few rows. In addition to rows being added it is possible for the content size to change because the height (content) of an existing row has been changed.
Supposing that I have a view controller whose main view contains two subviews: a button and a table view. Clicking the button results in the data store being modified and the table's reloadData method being called. When/Where would I assign a new value to the table's tableFooterView.bounds.size.height?
I should also point out that I am using UITableViewAutomaticDimension. If, in the table's data source delegate method cellForRowAt, I print the cell heights I get:
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 21.0
Upper table cell height = 44.0
All 21 except for the last one, the new one. This must be due to the automatic dimensioning not yet having been applied.
Update:
I have tentatively arrived at the following solution (many thanks to all of the folks on this thread for the biggest part of the solution). I am tentative because the solution involves calling reloadData twice in order to deal with an issue with the contentSize. See this GitHub project for a demo of the contentSize issue.
class TableView: UITableView {
override func reloadData() {
execute() { super.reloadData() }
}
override func reloadRows(at indexPaths: [IndexPath], with animation: UITableView.RowAnimation) {
execute() { super.reloadRows(at: indexPaths, with: animation) }
}
private func execute(reload: @escaping () -> Void) {
CATransaction.begin()
CATransaction.setCompletionBlock() {
if self.adjustFooter() {
reload() // Cause the contentSize to update (see GitHub project)
self.layoutIfNeeded()
}
}
reload()
CATransaction.commit()
}
// Return true(false) if the footer was(was not) adjusted
func adjustFooter() -> Bool {
guard let currentFrame = tableFooterView?.frame else { return false }
let newHeight = calcFooterHeight()
let adjustmentNeeded = newHeight != currentFrame.height
if adjustmentNeeded {
tableFooterView?.frame = CGRect(x: currentFrame.minX, y: currentFrame.minY, width: currentFrame.width, height: newHeight)
}
return adjustmentNeeded
}
private let minFooterHeight: CGFloat = 44
private func calcFooterHeight() -> CGFloat {
guard let footerView = tableFooterView else { return 0 }
let spaceTaken = contentSize.height - footerView.bounds.height
let spaceAvailable = bounds.height - spaceTaken
return spaceAvailable > minFooterHeight ? spaceAvailable : minFooterHeight
}
}