11

I've just discovered an astounding problem or counter-intuituve behaviour when using estimatedHeightForRowAtIndexPath

(1) My table has wildly varying row heights. The final results can range from 111 to about 400.

(2) I absolutely perfectly calculate each row height. I have these on hand in an array, that is to say cached.

{Note that this is exactly what, apparently, Apple engineers now recommend...example, point 5 .. Using Auto Layout in UITableView for dynamic cell layouts & variable row heights}

(3) When heightForRowAtIndexPath asks for a height, I do give it absolutely the correct height.

(4) WHen I build the cell, indeed, I build it to exactly the correct height (as in (2) and (3)).

{Note - of course it's iOS that finally sizes the height of a cell, not "me".}

THIS ALL WORKS PERFECTLY.

ie, each cell is built by iOS at exactly the height given in heightForRowAtIndexPath.

Now, I add the code ...

-(CGFloat)tableView:(UITableView *)tableView
    estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 120;
}

IN FACT, THE TABLE NO LONGER WORKS .................. THE ROW HEIGHTS BECOME RANDOM!!!

HAS ANYONE SEEN THIS INCREDIBLE BEHAVIOUR?

I did various tests to try to determine the relationship of what the hell estimatedHeightForRowAtIndexPath does. At first, I thought it might provide a lower bound on the height. So, 150 .. even my smaller cells would incorrectly be 150 height. But that is not the case.

I think it MIGHT be doing something like this: say your estimatedHeightForRowAtIndexPath value is 150. It is sometimes using 150 for rows that (in fact() prove to be that size or less, but sometimes it goes for the real size from heightForRowAtIndexPath.

On the other hand, if you put in a value for estimatedHeightForRowAtIndexPath that is smaller than will ever actually exist (say 100 in my example) it pretty much "utterly doesn't work" you just get what can only seem to be random heights on the cells.

very high cells seem to work correctly, perhaps something like "if the height from heightForRowAtIndexPath is double the estimated, then it does use the real height"

To be clear, it seems that it never makes a cell too small, but it often makes them too big.

To be clear, I am not using autolayout, it's the type of cell you just have to build. (I'm afraid I have no idea how this goes with autolayout.) This is Xcode5/iOS7+ only.

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 1
    The estimatedRow may be the max possible height ? 400 in your case. I think that the tableview delegate would found the maximum possible size of the tableview more useful than a minimum or a random value between those two. – KIDdAe Nov 27 '13 at 09:37
  • Doesn't work! When I use a large value (say 400), in a word, it "messes up more rows". Similarly if I use a small value (say 50) it "messes up more rows". Totally mysterious to me. – Fattie Nov 27 '13 at 09:39
  • Actually it could be if you set a ridiculously large value (2000), iOS just "totally ignored" the estimated value and then each row is the exact correct height as given in heightForRowAtIndexPath ... I'm not sure though. Astounding and fascinating issue!!! – Fattie Nov 27 '13 at 10:00
  • are you talking about visible rows being wrong or just some row that isn't on screen? – Daij-Djan Dec 04 '13 at 23:00
  • Right, the visible rows are wrong. (Regarding the rows not onscreen - I can't see them :) ) If you try it, you'll see it makes the heights "hopelessly wrong". – Fattie Dec 04 '13 at 23:04

2 Answers2

18

Updated Answer

After further investigation, it turns out "it simply requires Auto Layout" is incorrect. It was my sample code that required Auto Layout. I made a slight modification in DynamicHeightCell and it now works with or without Auto Layout.

Original Answer

It simply requires Auto Layout. Not surprising, really, but should absolutely be documented.

Here is a working project demonstrating tableView:estimatedHeightForRowAtIndexPath: working correctly with Auto Layout. If you turn off Auto Layout in the storyboard, it behaves as you've described.

Estimated Row Height Demo

Timothy Moose
  • 9,895
  • 3
  • 33
  • 44
  • Ugh. @JoeBlow I'm going to have to refute my own sloppy answer. See update. Please take the points back. There must be some side effect of `estimatedHeightForRowAtIndexPath` that breaks your implementation. I'm wondering if it's a reuse issue. Are the initial on-screen cells displayed correctly and then you see wrong behavior when you scroll? Can you post your height calculation and cell configuration code? – Timothy Moose Dec 07 '13 at 18:00
  • "I simply made 20 cells and used those" That is somewhat unusual. Can we see your 'cellForRowAtIndexPath` and `heightForRowAtIndexPath`? – Timothy Moose Dec 08 '13 at 17:16
  • Maybe crazy question, @JoeBlow and Timothy Moose: where are you loading the `UITableView`? In `viewDidLoad`, `viewDidAppear`, or `viewWillAppear`? I was having something similar. Changing my code to load the table in `viewWillAppear` solved my issue - but I'm not sure it's exactly what you're experiencing. I used `NSLog` and found my `estimatedHeightForRowAtIndexPath`, `heightForRowAtIndexPath`, and `cellForRowAtIndexPath` were running in some weird order. Once I moved where the table was being loaded, it worked fine. – leanne May 09 '14 at 22:27
1
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { }

This delegate method is usually used when you have numerous numbers of dynamic cells, which provides the rough estimation of the cell.

For exmaple: If you have 100 cells with each height ranging from 200 to 300 (e.g 201, 207, 250, 299, 300...) you can estimate the cell height of about 250. i.e.

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 250;
}
  • Providing an estimate the height of rows can improve the user experience when loading the table view.
  • If this delegate not implemented, it might be expensive to calculate all their heights and so lead to a longer load time.
Kusal Shrestha
  • 1,643
  • 2
  • 13
  • 20