Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve custom prototype cell height from storyboard?

Tags:

When using "Dynamic Prototypes" for specifying UITableView content on the storyboard, there is a "Row Height" property that can be set to Custom.

When instantiating cells, this custom row height is not taken into account. This makes sense, since which prototype cell I use is decided by my application code at the time when the cell is to be instantiated. To instantiate all cells when calculating layout would introduce a performance penalty, so I understand why that cannot be done.

The question then, can I somehow retrieve the height given a cell reuse identifier, e.g.

[myTableView heightForCellWithReuseIdentifier:@"MyCellPrototype"]; 

or something along that line? Or do I have to duplicate the explicit row heights in my application code, with the maintenance burden that follows?

Solved, with the help of @TimothyMoose:

The heights are stored in the cells themselves, which means the only way of getting the heights is to instantiate the prototypes. One way of doing this is to pre-dequeue the cells outside of the normal cell callback method. Here is my small POC, which works:

#import "ViewController.h"  @interface ViewController () {     NSDictionary* heights; } @end  @implementation ViewController  - (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath {     return [NSString stringWithFormat:@"C%d", indexPath.row]; }  - (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {     if(!heights) {         NSMutableDictionary* hts = [NSMutableDictionary dictionary];         for(NSString* reusableIdentifier in [NSArray arrayWithObjects:@"C0", @"C1", @"C2", nil]) {             CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height;             hts[reusableIdentifier] = [NSNumber numberWithFloat:height];         }         heights = [hts copy];     }     NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];     return [heights[prototype] floatValue]; }  - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {     return 3; }  - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {     return 1; }  - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {     NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];     UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype];     return cell; }  @end 
like image 209
Krumelur Avatar asked Dec 01 '12 13:12

Krumelur


1 Answers

For static (non-data-driven) height, you can just dequeue the cell once and store the height:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {     static NSNumber *height;     if (!height) {         UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];         height = @(cell.bounds.size.height);     }     return [height floatValue]; } 

For dynamic (data-driven) height, you can store a prototype cell in the view controller and add a method to the cell's class that calculates the height, taking into account the default content of the prototype instance, such as subview placement, fonts, etc.:

- (MyCustomCell *)prototypeCell {     if (!_prototypeCell) {         _prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCustomCell"];     }     return _prototypeCell; }  - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {     // Data for the cell, e.g. text for label     id myData = [self myDataForIndexPath:indexPath];      // Prototype knows how to calculate its height for the given data     return [self.prototypeCell myHeightForData:myData]; } 

Of course, if you're using custom height, you probably have multiple cell prototypes, so you'd store them in a dictionary or something.

As far as I can tell, the table view doesn't attempt to reuse the prototype, presumably because it was dequeued outside of cellForRowAtIndexPath:. This approach has worked very well for us because it allows the designer to modify cells layouts in the storyboard without requiring any code changes.

Edit: clarified the meaning of sample code and added an example for the case of static height.

like image 186
Timothy Moose Avatar answered Sep 30 '22 00:09

Timothy Moose