One of our application screens requires us to place a UICollectionView
inside of a UITableViewCell
. This UICollectionView
will have a dynamic number of items, resulting in a height which must be calculated dynamically as well. However, I am running into problems trying to calculate the height of the embedded UICollectionView
.
Our overarching UIViewController
was created in Storyboards and does make use of auto layout. But, I don't know how to dynamically increase the height of the UITableViewCell
based on the height of the UICollectionView
.
Can anyone give some tips or advice on how to accomplish this?
The right answer is YES, you CAN do this.
I came across this problem some weeks ago. It is actually easier than you may think. Put your cells into NIBs (or storyboards) and pin them to let auto layout do all the work
Given the following structure:
TableView
TableViewCell
CollectionView
CollectionViewCell
CollectionViewCell
CollectionViewCell
[...variable number of cells or different cell sizes]
The solution is to tell auto layout to compute first the collectionViewCell sizes, then the collection view contentSize, and use it as the size of your cell. This is the UIView method that "does the magic":
-(void)systemLayoutSizeFittingSize:(CGSize)targetSize
withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority
verticalFittingPriority:(UILayoutPriority)verticalFittingPriority
You have to set here the size of the TableViewCell, which in your case is the CollectionView's contentSize.
At the CollectionViewCell you have to tell the cell to layout each time you change the model (e.g.: you set a UILabel with a text, then the cell has to be layout again).
- (void)bindWithModel:(id)model {
// Do whatever you may need to bind with your data and
// tell the collection view cell's contentView to resize
[self.contentView setNeedsLayout];
}
// Other stuff here...
The TableViewCell does the magic. It has an outlet to your collectionView, enables the auto layout for collectionView cells using estimatedItemSize of the UICollectionViewFlowLayout.
Then, the trick is to set your tableView cell's size at the systemLayoutSizeFittingSize... method. (NOTE: iOS8 or later)
NOTE: I tried to use the delegate cell's height method of the tableView -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
.but it's too late for the auto layout system to compute the CollectionView contentSize and sometimes you may find wrong resized cells.
@implementation TableCell
- (void)awakeFromNib {
[super awakeFromNib];
UICollectionViewFlowLayout *flow = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
// Configure the collectionView
flow.minimumInteritemSpacing = ...;
// This enables the magic of auto layout.
// Setting estimatedItemSize different to CGSizeZero
// on flow Layout enables auto layout for collectionView cells.
// https://developer.apple.com/videos/play/wwdc2014-226/
flow.estimatedItemSize = CGSizeMake(1, 1);
// Disable the scroll on your collection view
// to avoid running into multiple scroll issues.
[self.collectionView setScrollEnabled:NO];
}
- (void)bindWithModel:(id)model {
// Do your stuff here to configure the tableViewCell
// Tell the cell to redraw its contentView
[self.contentView layoutIfNeeded];
}
// THIS IS THE MOST IMPORTANT METHOD
//
// This method tells the auto layout
// You cannot calculate the collectionView content size in any other place,
// because you run into race condition issues.
// NOTE: Works for iOS 8 or later
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority {
// With autolayout enabled on collection view's cells we need to force a collection view relayout with the shown size (width)
self.collectionView.frame = CGRectMake(0, 0, targetSize.width, MAXFLOAT);
[self.collectionView layoutIfNeeded];
// If the cell's size has to be exactly the content
// Size of the collection View, just return the
// collectionViewLayout's collectionViewContentSize.
return [self.collectionView.collectionViewLayout collectionViewContentSize];
}
// Other stuff here...
@end
Remember to enable the auto layout system for the tableView cells at your TableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
// Enable automatic row auto layout calculations
self.tableView.rowHeight = UITableViewAutomaticDimension;
// Set the estimatedRowHeight to a non-0 value to enable auto layout.
self.tableView.estimatedRowHeight = 10;
}
CREDIT: @rbarbera helped to sort this out