Jean Baptiste Jean Baptiste - 4 months ago 243
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

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

Img 1 : Main view

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

Img 2 : CollectionView

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

The second problem I have is about sizing the
UICollectionViewCell
, in fact I will not know in advance what the height of it is going to be (the width is the same as the
UITableView
). 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]()


the
UITableView
extension : Create the cell of the
UITableView
and set delegate of
UICollectionView
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]
cell.configureCell(post)

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
UICollectionView
: 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.configurePost(post.descriptionLbl)
cell.addSubview(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
UICollectionView


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
UITableViewCell
(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.
collectionView.reloadData()
}

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
UITableView
with a
tableView( ... heightForRowAtIndexPath ...)


I tried everything to make the
UITableViewCell
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
UICollectionViewCell
, is there any way to create an event when that happens? And maybe call
tableView( ... heightForRowAtIndexPath ...)
again?

Answer

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...) :

self.tableView.beginUpdates()
cellHeight[collectionView.tag] = cellHeightForPost[collectionView.tag][indexPath.row]
self.tableView.setNeedsLayout()
self.tableView.layoutIfNeeded()
self.tableView.endUpdates()

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])
Comments