I am curious when the UITableView\'s content size is updated after doing an insert/delete animation call. I figured it would be like most [UIView animation...] blocks in tha
Unfortunately, I haven't been able to find a good way to update the contentSize when adding/deleting rows from a UITableView either, but I did find a way to calculate what it will be if you know the index paths of the cells you are adding/removing.
Using the index path of the modified rows, you can calculate the heights of the respective cells with the tableView:heightForRowAtIndexPath: method and add it to the current content size height of the table view. Here's an example if you're adding just one row:
[self.tableView insertRowsAtIndexPaths:@[indexPathOfNewRow] withRowAnimation:UITableViewRowAnimationAutomatic];
CGFloat newCellHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPathOfNewRow];
CGFloat newContentSizeHeight = self.tableView.contentSize.height + newCellHeight
The content size will changed after calling delegates about height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
It looks like sizeThatFits()
can expose the height of a new contentSize
that's pending. You can then assign that pending size to have it resolve early for scroll animations.
With something like this:
extension UIScrollView {
var pendingContentSize: CGSize {
var tallSize = contentSize
tallSize.height = .greatestFiniteMagnitude
return sizeThatFits(tallSize)
}
func scrollToBottom(animated: Bool) {
contentSize = pendingContentSize
let contentRect = CGRect(origin: .zero, size: contentSize)
let (bottomSlice, _) = contentRect.divided(atDistance: 1, from: .maxYEdge)
guard !bottomSlice.isEmpty else { return }
scrollRectToVisible(bottomSlice, animated: animated)
}
}
I'm able to write view controller code like this:
tableView.insertRows(at: [newIndexPath], with: .none)
tableView.scrollToBottom(animated: true)
and have the table scroll all the way to the bottom (using the new content size) instead of it scrolling down to the 2nd-to-last row (using the old content size).