Create your cells with their own .xib. Add the labels, textview imageviews etc as you want. Make sure to pin all them with constraints horizontally and vertically to the leading/trailing/top and bottom of the cell.
Now the trick is to tell autolayout to layout the contentView of the cell, and return the minimum height to fit its content. Since heightForRow is executed before the cell is loaded in cellForRow, we need to use a fakeCell to calculate the height.
To do this lets say, in our controller, we have a tableView showing cells of class DetailsTableViewCell with a nib created for it DetailsTableViewCell.xib :
1) Create a @property in your controller for a fakeCell.
@property (nonatomic, strong) DetailsTableViewCell *fakeCell;
2) In viewDidLoad, initialize it from them nib:
self.fakeCell = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass:@"DetailsTableViewCell" owner:nil options:nil].firstObject;
3) In heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//grab fakeCell, style it with the data you want
[self.fakeCell styleWithDetails:self.details];
//and calculate the minimum height required to fit all content
return [self minimumCellHeightToSatisfyConstraints:self.fakeCell];
}
-(CGFloat)minimumCellHeightToSatisfyConstraints:(UITableViewCell *)cell {
//Makes sure cell's width is equal to tableView's width (can be wrong since they are in separated nibs)
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(self.tableView.bounds), CGRectGetHeight(cell.bounds));
//Force autolayout to recalculate all subview's frames
[cell layoutIfNeeded];
//Get the minimum height required for the cell to fit all its content
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
return height;
}
Important: If you forget to pin all subviews in the .xib vertically from top to bottom to the cell, calling systemLayoutSizeFittingSize:UILayoutFittingCompressedSize will return 0 and won't work.