17

Here's how I set my table:

self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.estimatedRowHeight = 150;
[self.view addSubview:self.tableView];

Within my cells, I call - (CGSize)sizeThatFits:(CGSize)size to programmatically return the height (which is set in - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. I'm not using constraints or autolayout.

Nothing complex and my heights are all perfectly laid out visually.

However the problem is when I push a new view controller, the cells jump/shift visually (either up or down). It seems to be jumping based on calculating the estimated row height values - yet sizeThatFits is also called for each visible cell before shifting so I'm really confused (not sure why either needs to be called at all really, since I'm leaving the view). I've checked the contentOffset for the tableView - it's unchanged so it's not the problem.

Vadoff
  • 9,219
  • 6
  • 43
  • 39

2 Answers2

6

Okay, I solved it by caching my cell heights in sizeThatFits, and returning that value for estimated cell heights within the delegate. Works beautifully.

Vadoff
  • 9,219
  • 6
  • 43
  • 39
  • Could you elaborate on your answer, @vadoff? I know of a bunch of workarounds, but not this one. – rainypixels Mar 24 '15 at 07:01
  • @rainypixels did you ever figure it out? – Steve Mar 30 '15 at 16:05
  • @Steve I have a bunch of options but I haven't picked one. I will probably go with caching the heights (key = ID of cell + Last Modified Date, value = height) using NSCache. I was hoping vadoff had a different/more elegant solution. – rainypixels Apr 01 '15 at 16:01
  • @rainypixels mine was caused by calling reloadData in viewWillAppear, that causes the estimated height delegate to be called (which was wildly inaccurate) so all the cells offscreen and above the contentOffset where really small, now I just return a cached height that I got from the cell.frame in willDisplayCell delegate and stored in a dictionary, it works great, need to remove the cached height when cell data changes but I just replace it with an average cell height for the reuseIdentifier which is more accurate than before. So far no issues and always smooth scrolling. – Steve Apr 01 '15 at 16:43
  • Brilliant solution. Thanks! – Accatyyc Jul 06 '15 at 11:59
4

Quick fix:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    self.tableView.reloadData()
}

Edit: After spending hours on this and similar issues I've found the best solution is to cache the cell heights and return them in estimatedHeightForRowAtIndexPath delegate method, the problem is caused by estimated heights being really inaccurate.

I cached the heights in tableView(_:willDisplayCell:forRowAtIndexPath:) into a dictionary with the unique ID's for the data as keys this way when data gets added or updated I can just remove that height from the cache and use better estimated heights so only that cell uses an estimated height. So far this solves all my jumping and scrolling issues.

Steve
  • 2,526
  • 2
  • 20
  • 30