Almost every time I write an app for a client I have to implement some kind of 'hack' to get UITableViewCell
s to dynamically become the right height. Depending on the content of the cells this can vary in difficulty.
I usually end up running through code that formats the cell twice, once in heightForRowAtIndexPath:
and then again in cellForRowAtIndexPath:
. I then use an array or dictionary to store either the height or the formatted cell object.
I've probably written and rewritten this code 20 times over the past 2 years. Why did Apple implement it in this order? It would be much more straightforward to configure the cells and THEN set the height either in cellForRowAtIndexPath:
or shortly thereafter in heightForRowAtIndexPath:
.
Is there a good reason for the existing order? Is there a better way to handle it?
Best guess: UITableView
needs to know the total height of all cells so that it can know the percentage of scroll for the scroll bar and other needs.
Actually, in iOS 7, it doesn't have to work that way. You can now set an estimated height for your rows and calculate each row's real height only when that row is actually needed. (And I assume that this is exactly because people made complaints identical to yours: "Why do I have to do this twice?")
Thus you can postpone the height calculation and then memoize it the first time that row appears (this is for a simple one-section table, but it is easy to adapt it if you have multiple sections):
- (void)viewDidLoad {
[super viewDidLoad];
// create empty "sparse array" of heights, for later
NSMutableArray* heights = [NSMutableArray new];
for (int i = 0; i < self.modeldata.count; i++)
[heights addObject: [NSNull null]];
self.heights = heights;
self.tableView.estimatedRowHeight = 40; // new iOS 7 feature
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int ix = indexPath.row;
if ([NSNull null] == self.heights[ix]) {
h = // calculate _real_ height here, on demand
self.heights[ix] = @(h);
}
return [self.heights[ix] floatValue];
}
You supplied an estimated height, so all the heights are not asked for beforehand. You are asked for a height only before that row actually appears in the interface, either because it is showing initially or because you or the user scrolled to reveal it.
NOTE Also, note that if you use dequeueReusableCellWithIdentifier:forIndexPath:
the cell you get has the correct final height already. That is the whole point of this method (as opposed to the earlier mere dequeueReusableCellWithIdentifier:
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With