jgongo
7/26/2013 - 4:06 PM

Dynamic row height calculation in table view with cells implemented with nib + autolayout

Dynamic row height calculation in table view with cells implemented with nib + autolayout

// Sample usage:
// 1: Create a cell "MyCell" with a nib using autolayout.
// 2: The following table view controller code will automatically size the cells according to the constraints and the content.

@interface MyTableViewController : JSDynamicRowHeightTableViewController

@end

@implementation MyTableViewController

- (id)init
{
    if ((self = [super init]))
    {
        self.cellNibName = @"MyCell";
    }

    return self;
}

- (id)cellDataAtIndexPath:(NSIndexPath *)indexPath
{
    return self.data[indexPath.row];
}

- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    id myCellData = [self cellDataAtIndexPath:indexPath];

    cell.data = myCellData;
}

@end
//
//  JSDynamicRowHeightTableViewController.m
//
//  Created by Javier Soto on 7/25/13.
//  Copyright (c) 2013 JavierSoto. All rights reserved.
//

#import "JSDynamicRowHeightTableViewController.h"

@interface JSDynamicRowHeightTableViewController ()
{
    UITableViewCell *_cellForRowHeightCalculation;
}

@property (nonatomic, readonly, strong) UITableViewCell *cellForRowHeightCalculation;

@end

@implementation JSDynamicRowHeightTableViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSAssert(self.cellNibName, @"You must set the name of the nib that will be used in the cells of this controller");

    UINib *cellNib = [UINib nibWithNibName:self.cellNibName
                                    bundle:nil];

    [self.tableView registerNib:cellNib
         forCellReuseIdentifier:self.cellNibName];
}

- (UITableViewCell *)cellForRowHeightCalculation
{
    if (!_cellForRowHeightCalculation)
    {
        UINib *offscreenMessageNib = [UINib nibWithNibName:self.cellNibName bundle:nil];
        _cellForRowHeightCalculation = [offscreenMessageNib instantiateWithOwner:self options:nil][0];
    }

    return _cellForRowHeightCalculation;
}

- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    [self doesNotRecognizeSelector:_cmd];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.cellNibName];

    [self configureCell:cell
      forRowAtIndexPath:indexPath];

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *offscreenCellForHeightCalculation = self.cellForRowHeightCalculation;

    CGRect cellFrame = offscreenCellForHeightCalculation.frame;
    cellFrame.size.width = self.tableView.frame.size.width;
    offscreenCellForHeightCalculation.frame = cellFrame;

    [self configureCell:offscreenCellForHeightCalculation
      forRowAtIndexPath:indexPath];

    CGFloat cellHeight = [offscreenCellForHeightCalculation systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    return cellHeight;
}

@end
//
//  JSDynamicRowHeightTableViewController.h
//
//  Created by Javier Soto on 7/25/13.
//  Copyright (c) 2013 JavierSoto. All rights reserved.
//

#import <UIKit/UIKit.h>

/**
 This class automatically implements `-tableView:heightForRowAtIndexPath:`. The provided cell nib (see `cellNibName`) must use autolayout.
 */
@interface JSDynamicRowHeightTableViewController : UITableViewController

/**
 You must call this method as soon as the controller is instantiated to set the name of the nib that will be used for the cells of this class.
 */
@property (nonatomic, readwrite, copy) NSString *cellNibName;

/**
 You can implement this simpler method to just pass information to the cell to display the appropiate contents for the index path.
 @param cell The instantiated cell from the specified nib (see `-initWithStyle:cellNibName:`).
 */
- (void)configureCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;

@end