Jean Baptiste Jean Baptiste - 3 months ago 139
Swift Question

Swift: Expand UITableViewCell height depending on the size of the UICollectionView inside it

Hi everyone. I started learning programming 1 month ago so please be nice if I don't explain my problem well :)

My project is composed of a main

. In each cell of the
, I have a
(on horizontal scrolling).

Img 1 : Main view

The width of each
is the same as the entire
. My first problem is about sizing the height of the
(which will depend of the size of the
itself and the size of the content on top of it).

Img 2 : CollectionView

This has to be done automatically. In fact, the
s will not have the same height, so when the user will scroll the
horizontally, a new
will appear (with different height) and the height of the
will have to adapt.

The second problem I have is about sizing the
, in fact I will not know in advance what the height of it is going to be (the width is the same as the
). I should import the content from a nib.

So now here is my ViewController file,

the variables:

@IBOutlet weak var tableView: UITableView!
var storedOffsets = [Int: CGFloat]()

extension : Create the cell of the
and set delegate of
inside it

extension IndexVC: UITableViewDelegate, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6 //Temp

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCellWithIdentifier("CellCollectionView") as? CellPost {
let post = self.posts[indexPath.row]

return cell
} else {
return CellPost()


func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }

tableViewCell.setCollectionViewDataSourceDelegate(self, forRow: indexPath.row)
tableViewCell.collectionViewOffset = storedOffsets[indexPath.row] ?? 0

func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
guard let tableViewCell = cell as? CellPost else { return }

storedOffsets[indexPath.row] = tableViewCell.collectionViewOffset

the part of the
: Add the view from a Nib/xib to the cell

extension IndexVC: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 9 //Temp

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cellInCollectionView", forIndexPath: indexPath)

if let textPostView = NSBundle.mainBundle().loadNibNamed("textPostView", owner: self, options: nil).first as? textPostView {
textPostView.translatesAutoresizingMaskIntoConstraints = false
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["view":textPostView]))


return cell

Make the size of the cell the same as the entire

extension IndexVC: UICollectionViewDelegateFlowLayout {
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSizeMake(collectionView.frame.width, collectionView.frame.height)

And I created this extension in the class dedicated for the
(not useful for the problem, but if any of you want to recreate it)

extension CellPost {
func setCollectionViewDataSourceDelegate<D: protocol<UICollectionViewDataSource, UICollectionViewDelegate>>(dataSourceDelegate: D, forRow row: Int) {

collectionView.delegate = dataSourceDelegate
collectionView.dataSource = dataSourceDelegate
collectionView.tag = row
collectionView.setContentOffset(collectionView.contentOffset, animated:false) // Stops collection view if it was scrolling.

var collectionViewOffset: CGFloat {
set {
collectionView.contentOffset.x = newValue

get {
return collectionView.contentOffset.x

If anyone want to use this code, it works great, but I have to hardcode the height of the
with a
tableView( ... heightForRowAtIndexPath ...)

I tried everything to make the
adapt to what's inside it (I tried calculating the size of content sent by the Nib I'm putting in the cell, and then send it to
tableView( ... heightForRowAtIndexPath ...)

but I can't make it work. I also tried Auto Layouts but maybe I'm doing it wrong. I also think the problem could come from the part that I imported the nib in my cell.

I also have no idea of a way to expand the cell after the user has swiped the
, is there any way to create an event when that happens? And maybe call
tableView( ... heightForRowAtIndexPath ...)


In order to solve my problem, I created an array to store the height of each cell of the UITableView (named cellHeight).

I also calculate the height of each cell at the start of the program in an array composed of array (named cellHeightForPost). The main array represent all the TableView cells, each array inside it represent the height of each cell of the 'collectionView'

In order to update the table view everytime I change the collectionView inside it, i used the fonction collectionView(... willDiplayCell...) :

cellHeight[collectionView.tag] = cellHeightForPost[collectionView.tag][indexPath.row]

My UITableView size is defined in tableView(... heightForRowAtIndexPath) and tableView(... estimatedHeightForRowAtIndexPath) :

return cellHeight[indexPath.row]

To set the size of the collectionViewCells :

return CGSize(width: collectionView.frame.width, height: cellHeightForPost[collectionView.tag][indexPath.row])