issue while set dynamic width of collection view cell

I am trying to dynamically set the width of collection view cell. Initially it's not rendering as expected. But when I tap on the cell, its getting adjusted as I want. Here's the code that I wrote:

Code

import UIKit

class ViewController: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource {

    @IBOutlet weak var collView: UICollectionView!


    var tasksArray = ["To Do", "SHOPPING","WORK"]
    var selectedIndex = Int()

    override func viewDidLoad() {
        super.viewDidLoad()
        let layout = collView?.collectionViewLayout as! UICollectionViewFlowLayout
        layout.itemSize = UICollectionViewFlowLayout.automaticSize
        layout.estimatedItemSize = CGSize(width: 93, height: 40)
        // Do any additional setup after loading the view, typically from a nib.
    }
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return tasksArray.count
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
        cell.lblName.text = tasksArray[indexPath.row]
        if selectedIndex == indexPath.row
        {
            cell.backgroundColor = UIColor.lightGray
        }
        else
        {
            cell.backgroundColor = UIColor.white
        }
        cell.layer.borderWidth = 1
        cell.layer.cornerRadius = cell.frame.height / 2
        return cell
    }
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        selectedIndex = indexPath.row
        self.collView.reloadData()
    }
}

here i am attaching two image before tapping and after tapping so you can easily understood

[![Here is the image before i tap

on cell]2]2

so please tell me whats wrong in my code


Inside your CollectionViewCell override preferredLayoutAttributesFitting function This is where the cell has a chance to indicate its preferred attributes, including size, which we calculate using auto layout.

 override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    setNeedsLayout()
    layoutIfNeeded()
    let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
    var frame = layoutAttributes.frame
    frame.size.width = ceil(size.width)
    layoutAttributes.frame = frame
    return layoutAttributes
}

Questions: Im trying to create a collection view with cells displaying string with variable length. Im using this function to set cell layout: func collectionView(collectionView : UICollectionView,layout collectionViewLayout:UICollectionViewLayout,sizeForItemAtIndexPath indexPath:NSIndexPath) -> CGSize { var cellSize:CGSize = CGSizeMake(self.whyCollectionView.frame.width, 86) return cellSize


I have found a small trick for swift 4.2

For dynamic width & fixed height:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let label = UILabel(frame: CGRect.zero)
    label.text = textArray[indexPath.item]
    label.sizeToFit()
    return CGSize(width: label.frame.width, height: 32)
}

For example, maybe we want our cell’s width to be equal to the collection view’s width, or maybe we want some cells to be self-sizing, but others to not be. The solution is pretty simple — create a width and/or height constraint on your cell’s contentView and create setters to set their value from outside the cell.


It is obvious that you have to use sizeForItemAt flow layout delegate in order to pass the dynamic width. But the tricky part is to calculate the width of the cell based on the text. You can actually calculate the width of a text given that you have a font.

Let's introduce few extension which will help us along the way

StringExtensions.swift

extension String {

    public func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect,
                                        options: .usesLineFragmentOrigin,
                                        attributes: [.font: font], context: nil)

        return ceil(boundingBox.width)
    }
}

This method let us know the width of a string, if i provide it the height and the font. Then use it inside sizeForItem as follows

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let height = 40
    let text = YOUR_TEXT
    let width = text.width(withConstrainedHeight: height, font: Font.regular.withSize(.extraSmall)) + EXTRA_SPACES_FOR_LEFT_RIGHT_PADDING
    return CGSize(width: width, height: height)
}

While it is possible to implement this with collection views - we’d have to do a lot of the heavy lifting ourselves to achieve it. Another item which may seem quite trivial is the automatic width sizing table views apply to their cells so they span across the whole table view.


After configuring it, we call layoutIfNeeded on the cell to setup the AutoLayout. The final step is to actually get the size. We use UILayoutFittingCompressedSize to specify that we want to use the smallest possible size, and set it’s width to be the static width of our cells.


Your cell heights will need to be dynamic, based on the content of each cell. You’ll start by implementing the dynamic cell heights in AuteurListViewController. Creating Self-Sizing Table View Cells. To get dynamic cell heights working properly, you need to create a custom table view cell and set it up with the right Auto Layout constraints.


That’s all you need in order to layout a collection view with flexible cell sizes, in Swift. While the code might look scary at first sight, things are actually pretty simple. Hopefully, Apple will introduce a nicer way of achieving this dynamic layout, without all this boilerplate code.