CollectionView Dynamic cell height swift

You can dynamically set the frame of the cell in the cellForItemAtIndexPath function, so you can customize the height based on a label if you disregard the sizeForItemAtIndexPath function. With customizing the size, you'll probably have to look into collection view layout flow, but hopefully this points you in the right direction. It may look something like this:

class CollectionViewController: UICollectionViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    var array = ["a","as","asd","asdf","asdfg","asdfgh","asdfghjk","asdfghjklas","asdfghjkl","asdghjklkjhgfdsa"]
    var heights = [10.0,20.0,30.0,40.0,50.0,60.0,70.0,80.0,90.0,100.0,110.0] as [CGFloat]

    override func viewDidLoad() {
        super.viewDidLoad()
    }


    override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return array.count
    }

    override func collectionView(collectionView: UICollectionView,
        cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

            let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CellID", forIndexPath: indexPath) as Cell
            cell.textLabel.text = array[indexPath.row]
            cell.textLabel.sizeToFit()

            // Customize cell height
            cell.frame = CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, heights[indexPath.row])
            return cell
    }

    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
        return CGSizeMake(64, 64)
    }
}

which gives dynamic heights like so enter image description here


In Swift 3, use the below method:

private func updateCollectionViewLayout(with size: CGSize) {
    var margin : CGFloat = 0;
    if isIPad {
        margin = 10
    }
    else{
        margin = 6
        /* if UIDevice.current.type == .iPhone6plus || UIDevice.current.type == .iPhone6Splus || UIDevice.current.type == .simulator{
         margin = 10

         }
         */
    }
    if let layout = menuCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
        layout.itemSize = CGSize(width:(self.view.frame.width/2)-margin, height:((self.view.frame.height-64)/4)-3)
        layout.invalidateLayout()
    }
}

While the answer above may solve your problem, it establishes a pretty crude way of assigning each cells height. You are being forced to hard code each cell height based on some estimation. A better way of handling this issue is by setting the height of each cell in the collectionview's sizeForItemAtIndexPath delegate method.

I will walk you through the steps on how to do this below.

Step 1: Make your class extend UICollectionViewDelegateFlowLayout

Step 2: Create a function to estimate the size of your text: This method will return a height value that will fit your string!

private func estimateFrameForText(text: String) -> CGRect {
    //we make the height arbitrarily large so we don't undershoot height in calculation
    let height: CGFloat = <arbitrarilyLargeValue>

    let size = CGSize(width: yourDesiredWidth, height: height)
    let options = NSStringDrawingOptions.UsesFontLeading.union(.UsesLineFragmentOrigin)
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(18, weight: UIFontWeightLight)]

    return NSString(string: text).boundingRectWithSize(size, options: options, attributes: attributes, context: nil)
}

Step 3: Use or override delegate method below:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    var height: CGFloat = <someArbitraryValue>

   //we are just measuring height so we add a padding constant to give the label some room to breathe! 
    var padding: CGFloat = <someArbitraryPaddingValue>

    //estimate each cell's height
    if let text = array?[indexPath.item].text {
         height = estimateFrameForText(text).height + padding
    }
    return CGSize(width: yourDesiredWidth, height: height)
}