Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scroll UITableView to bottom when using estimated row heights

I'm having a problem that seems like it shouldn't really be a problem. I'm trying to create a comments ("chat") view for my app, but I'm using estimated row heights and can't find a nice way to have the comments start at the bottom, such that when the view is loaded, the latest comment is at the bottom of the screen, just above the input area also at the bottom of the screen.

I've been looking around for ages and have found solutions like these:

[self.tblComments scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

Doesn't work nicely because it uses the estimated row height, which isn't always right (some comments will be much longer).

[self.tblComments setContentInset:UIEdgeInsetsMake(self.tblComments.bounds.size.height - self.tblComments.contentSize.height, 0, 0, 0)];

Same problem as above (contentSize is determined by estimatedRowHeight).

I'm sure there's a perfect solution to this somewhere since so many apps have views like this, I just can't find it. I'd love any insight into something I may be missing here...

like image 345
krishan711 Avatar asked Oct 31 '22 13:10

krishan711


1 Answers

Edit:

contentSize is simply "The size of the content view." But since you've set a value for estimatedRowHeight, all your off-screen cells are assumed to be of height estimatedRowHeight.

According to the docs, here's the point of estimatedRowHeight:

Providing a nonnegative estimate of the height of rows can improve the performance of loading the table view. If the table contains variable height rows, it might be expensive to calculate all their heights when the table loads. Using estimation allows you to defer some of the cost of geometry calculation from load time to scrolling time.

So basically, in order to save time, the table is making an assumption about row height and thus content size based on your estimatedRowHeight. By using estimatedRowHeight you're asking your UITableView to make these "shortcuts" in order to improve performance. You're basically telling the program not to calculate the row heights ahead of time, so in order to get the row heights and content sizes of any off-screen content, you have to calculate them manually. As long as you decide to set an estimatedRowHeight, this is unavoidable.

My guess is that many chat apps you've looked at don't use estimatedRowHeight in order to avoid this issue...

But if you do in fact feel as if using estimatedRowHeight helps with your app's performance, here are two suggestions.

SUGGESTION #1

Perhaps it makes sense to have a totalChatHeight variable where you keep track of the total row heights as they come in, calculated using similar logic as your heightForRowAtIndexPath: method. By originally calculating this info in the background as the comments load then incrementing as you go, you'd still maintain the performance benefits of using estimatedRowHeight without sacrificing the functionality of having the table know its row heights.

By doing this, you can structure your contentOffset statement like so to scroll to the bottom using the known table row height information:

CGPoint bottomOffset = CGPointMake(0, totalChatHeight - self.tblComments.frame.size.height);
[self.tblComments setContentOffset:bottomOffset animated:YES];

SUGGESTION #2

Another suggestion would be to calculate your row average dynamically so you have a more precise measurement for estimatedRowHeight and the contentSize of your UITableView would approximately be the same with or without using estimatedRowHeight. That way estimatedRowHeight is actually the estimate row height and the estimated content size should also be near the actual contentSize, so this standard contentOffset formula should work:

CGPoint bottomOffset = CGPointMake(0, self.tblComments.contentSize.height - self.tblComments.frame.size.height);
[self.tblComments setContentOffset:bottomOffset animated:YES];

This could potentially be dangerous though as both the chat log and rounding error grow.

SUGGESTION #3 <-- Perhaps the best way to go in this case

This third suggestion may be the easiest to implement in your case as it doesn't require any ongoing calculations:

Just set estimatedRowHeight to the last row's height before reloading the table's data.

You can calculate that new row's height using whatever algorithm you use in heightForRowAtIndexPath: then set estimatedRowHeight to that height; that way you can use this statement to scroll to the last cell

[self.tblComments scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

and even though, as you said, it determines the row position based on estimatedRowHeight, it should scroll to the correct position as long as the estimatedRowHeight equals the bottom row height.

like image 169
Lyndsey Scott Avatar answered Nov 15 '22 07:11

Lyndsey Scott