问题
I am using the estimatedRowHeight
method from UITableView in iOS 7, which works perfectly for fast loading of a UITableView with 5000 rows of variable heights.
My UITableView consists of 50 sections. Every section has 100 rows, of variable height.
At the start I use estimatedRowHeight
for fast-loading, but after that when I call scrollToRowAtIndexPath
, my UITableView scrolls to the wrong offset. I can understand why this is, because it has an estimatedRowHeight
until I scroll the entire table and the proper cell heights are set in the heightForRowAtIndexPath
delegate method.
Any solution?
回答1:
Unfortunately this issue is to be expected when using estimatedRowHeight
in your code with such a low value. When you scrollToRowAtIndexPath
it does not actively calculating the correct size as you go. The underlying cause is, if you scrolled from Section 1 to Section 2, it could feasibly calculate the correct position on the fly and the estimatedRowHeight
of the cells if it was a relatively new device. Any older devices would be brought to their knees, and even any new ones would be as well if you had to process 5000 cells, for example.
A maybe-solution to your problem could be to increase the estimatedRowHeight
constant so the device doesn't have to do as much work.
回答2:
When scrollToRowAtIndexPath
is called, height of all the cells b/w current position and target offset are not calculated. Only some of them are.
Instead, UITableView uses estimatedRowHeight
to calculate target offset which leads to wrong offset.
I'm facing the same issue and I found a small trick(which I don't really like) to calculate exact cell height only once after initial relaodData
. I inserted two lines below:
tableView.reloadData()
// only for the initial reloadData
let numSections = tableView.numberOfSections
let numRowsInLastSection = tableView.numberOfRowsInSection(numSections-1)
// scrolls to the last row in the tableView
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: numRows-1, inSection: numSections-1), atScrollPosition: .Bottom, animated: false)
// back again to the original position(which is 0 for this is only called right after the initial reloadData)
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: .Top, animated: false)
This results in the tableView appearing on the screen late, but I think it's acceptable rather than freezing UI after the tableView appeared. It's even better when you call an API on the beginning because it feels like a little delay on the network, not the UI lag.
After this, tableView jumps to exact position of the cells.
Yeah, I don't really like this solution either :|
EDIT:
The only reason I did this was to calculate actual row height by calling cellForRowAtIndexPath:
, but I found out when giving proper estimatedHeight for every row by delegate method estimatedHeightForRowAtIndexPath:
instead of giving static value to UITableView.estimatedRowHeight
solves the issue.
What I finally did was to cache the heights of rows from willDisplayCell:forRowAtIndexPath:
to disk and use that value on estimatedHeightForRowAtIndexPath:
.
By doing this, estimatedHeightForRowAtIndexPath:
for all rows are called in the beginning and scrollToRowAtIndexPath
works well.
回答3:
It's not the prettiest solution but I use a workaround for this issue who do the trick. Always update the estimatedRowHeight to the tallest computed cell and then use something like this :
- Scroll the content of the tableView to offset zero.
Finish by calling scrollToRowAtIndexPath to index path zero.
[self.tableView setContentOffset:CGPointZero animated:YES]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; });
As result the tableView should scroll to the top quite smoothly.
回答4:
The solution for me was to set the estimatedRowHeight to 0
回答5:
This solved my problem
let oldContentSize = tableView.contentSize
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
// Called again when tableView contentSize change
if !tableView.contentSize.equalTo(oldContentSize) {
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
回答6:
Still no solid solution but for me it was acceptable to perform a scroll without animation which worked well in the sense that it scrolls to the expected cell with the correct offset, despite of what is mentioned in http://www.openradar.me/20829131.
self.tableView.scrollToRowAtIndexPath(indexPath, atScrollPosition:.Top, animated: false)
回答7:
It seems to work after the view was layouted by the system. I'm calling scrollToRowAtIndexPath with correct results in the VC's viewDidAppear. Which is not very beautiful of course, because it scrolls when the view is already visible...
回答8:
try to add this code in -cellForRowAtIndexPath: right before return the cell
[cell layoutIfNeeded]
来源:https://stackoverflow.com/questions/20892661/uitableview-scrolltorowatindexpath-scrolls-to-wrong-offset-with-estimatedrowheig