UITableView scrolling to the end corrupted with EstimatedRowHeight

I am trying to scroll the bottom of my UITableView (commentsFeed) whenever the user creates a new comment or the user updates the UITableView.

The code I use is:

func scrollToBottomOfComments() { var lastRowNumber = commentsFeed.numberOfRowsInSection(0) - 1 var indexPath = NSIndexPath(forRow: lastRowNumber, inSection: 0) commentsFeed.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) } 

The problem here is in viewDidLoad :

 commentsFeed.rowHeight = UITableViewAutomaticDimension commentsFeed.estimatedRowHeight = 150 

This basically means that comments can have dynamic heights, as users can post either very long comments or very short comments.

When I use the estimated scrollToBottom parameter, my scrollToBottom does not scroll down because it basically assumes my table height is commentsFeed.count * commentsFeed.estimatedRowHeight

This is not true.

When I delete estimatedRowHeight , although it doesn’t work either, and I think the reason is that it does not have the line height calculated correctly, because each line has dynamic heights.

How to soften this?

Edit: it should be said that the scroll does not end in the correct position, but at the moment when I use my finger to scroll anywhere, then the data jumps to the place where it should have been through the scroll

+5
source share
4 answers

Why don't you calculate the actual row size with something similar to the method below.

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CustomObject *message = [list.fetchedObjects objectAtIndex:indexPath.row]; //fontChat not available yet NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:message]; NSRange all = NSMakeRange(0, text.length); [text addAttribute:NSFontAttributeName value:[UIFont fontWithName:DEFAULT_FONT size:21] range:all]; [text addAttribute:NSForegroundColorAttributeName value:RGB(61, 61, 61) range:all]; CGSize theSize = [text boundingRectWithSize:CGSizeMake(200, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size; if (theSize.height == 0) { theSize.height = FIXED_SIZE; } return theSize.height; } 

Now I use the following source for scrolling: Check this out.

 -(void) scrollTolastRow { if (self.tableView.contentSize.height > self.tableView.frame.size.height) { CGPoint offset = CGPointMake(0, self.tableView.contentSize.height - self.tableView.frame.size.height); [self.tableView setContentOffset:offset animated:YES]; } } 
+6
source

There are a few things that can affect your outcome, but most likely your approximate height is not a big estimate. In my experience, anything not particularly close to the true cell height can lead to chaos in the animation. In your case, you indicate that the content is comments or free-form text. I would suggest that these cell heights vary greatly, and depending on how your cell is generated, you probably will not be able to provide a very accurate estimate, and therefore you should not provide it. For a large number of cells, this will degrade performance, but you probably have no choice. Instead, you may need to shift your focus to how you can place pages in a cell / page in your spreadsheet to avoid costly calculations, or change the order of your cell to be able to calculate the best score. Another suggestion could be an implementation of estimatedHeightForRowAtIndexPath with a precise, but still less complicated algorithm for calculating the height. In any case, a constant value for the estimated RawHeight will never work with DynamicType support. At least you need to consider the current size of DynamicType.

Other than this, what can you do? Instead of using UITableViewAutomaticDimension consider embedding heightForRowAtIndexPath and calculating the height of the displayed rows and caching the result (you can use the NSIndexPath -> NSNumber NSCache ). You need to cache the result, because without estimatedHeight , heightForRow is called once for each row when the table is loaded, and then once for each cell as it appears on the screen. When using estimatedHeight on iOS 8, estimatedHeight is called once for each cell at startup, and heightForRow is called when cells appear. Here the score is critical because this is what is used to calculate the contentSize UITableView UIScrollView support. If the calculated size is wrong, the contentSize wrong, and therefore, when you request that the tableView scroll to the last cell, the frame of the last cell is calculated with a poor rating, which gives the wrong contentOffset . Unfortunately, I suppose (based on the behavior that I see trying to reproduce your question) that when using UITableViewAutomaticDimension without an estimate, the runtime implicitly evaluates the estimate.

+2
source

EstimatedRowHeight is used by UIKit to evaluate the whole contentSize (and scrollIndicatorInset), so if you add a new row at the end with automatic row measurement, you must reset the ratedRowHeight to the actual average of the entire tableView before the scroll animation.

This is not a good solution, because its tasks are easier to calculate the line height in the old style - manually. Or add a new cell height value to the height of the contents of the old table.

But since you are adding a new row to the end column, you can scroll the middle or top position that ends with the bottom position, because there is contentInset.bottom = 0. And also the animation will look better.

So:

 commentsFeed.scrollToRowAtIndexPath(indexPath, atScrollPosition: .**Middle**, animated: true) 

It seems that in the .Middle and .Top positions there is some condition under the hood in the animation, which interferes with making a gap between the bottom edge and the contents of the table (+ contentInset.bottom)

PS Why not use manuall line height calculation?

Because there is autorun, and I believe that “auto” is a shortcut to automatic. It will also save your time and problems with custom fonts, assign a row, a combo box with a lot of tags and other subheadings, etc.

+1
source

Try this, works for me:

 NSMutableArray *visibleCellIndexPaths = [[self.tableView indexPathsForVisibleRows] mutableCopy]; [self.tableView beginUpdates]; [self.tableView reloadRowsAtIndexPaths:visibleCellIndexPaths withRowAnimation:UITableViewRowAnimationNone]; [self.tableView endUpdates]; [self.tableView scrollRectToVisible:CGRectMake(0, self.tableView.contentSize.height - self.tableView.bounds.size.height, self.tableView.bounds.size.width, self.tableView.bounds.size.height) animated:YES]; 
0
source

Source: https://habr.com/ru/post/1212671/


All Articles