How to set the size of a UICollectionViewCell depending on the dynamic size of the UIlabel in it using SWIFT programmatically?

I have a job where I have to prepare a simple GridView with a few limitations. I was thinking about using UICollectionView, and also did some research online. I found one blog post here that does what I want to achieve.

Now it uses a class customCollectionViewCellfor rows and columns. Each cell has in it UILabel. Thus, it UILabelcan have dynamic text, the cell must adjust its size in accordance with dynamism UILabel.

Please find all the code below -

UICollectionView for cell row

class RowCell: UICollectionViewCell
{
    var textLabel : UILabel!

    override init(frame: CGRect)
    {
        super.init(frame: frame)

        textLabel = UILabel(frame: CGRectMake(0, 0, 65, 35))
        textLabel.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
        textLabel.textAlignment = .Center
        textLabel.numberOfLines = 0
        textLabel.backgroundColor = UIColor.clearColor()
        textLabel.lineBreakMode = NSLineBreakMode.ByCharWrapping
        textLabel.sizeThatFits(CGSizeMake(self.bounds.width, self.bounds.height))

        contentView.addSubview(textLabel)
        self.layer.borderWidth = 0.7
        self.layer.borderColor = charcoalColor.CGColor
        self.frame = CGRectMake(frame.origin.x, frame.origin.y, textLabel.frame.width*8, textLabel.frame.height*8)

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

UICollectionView for column cell

class ColumnCell: UICollectionViewCell
{
    var columnLabel : UILabel!

    override init(frame: CGRect)
    {
        super.init(frame: frame)

        columnLabel = UILabel()
        columnLabel.frame = CGRectMake(0 ,0 ,65 ,35)
        columnLabel.font = UIFont.systemFontOfSize(UIFont.smallSystemFontSize())
        columnLabel.textAlignment = .Center
        columnLabel.backgroundColor = UIColor.clearColor()
        columnLabel.numberOfLines = 0
        columnLabel.lineBreakMode = NSLineBreakMode.ByCharWrapping
        columnLabel.sizeThatFits(CGSizeMake(self.bounds.width, self.bounds.height))


        contentView.addSubview(columnLabel)

        self.layer.borderWidth = 0.7
        self.layer.borderColor = charcoalColor.CGColor

        self.frame = CGRectMake(frame.origin.x, frame.origin.y, columnLabel.frame.width*8, columnLabel.frame.height*8)

    }


    func getDynamicHeight(ofLabel label: UILabel)->CGFloat
    {
        if(label.text == nil)
        {
        label.sizeToFit()
        var maxLabelSize = CGSizeMake(label.bounds.size.width, CGFloat(MAXFLOAT))
        var expectedSize = (label.text! as NSString).boundingRectWithSize(maxLabelSize, options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName:label.font], context: nil).size

        return expectedSize.height
        }

        return CGFloat(30)

    }


    required init(coder aDecoder: NSCoder)
    {
        fatalError("init(coder:) has not been implemented")
    }

}

My ViewController in which I create a CollectionView

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate
{

    let columnCellIdentifier = "ColumnCellIdentifier"
    let rowCellIdentifier    = "RowCellIdentifier"
    let arrayOfData = ["Tom", "Han", "Jerry", "Popye", "Bluto", "Elvis", "Vin", "PaulShankar Dharmawat", "Vishwanathan", "Belloweiss"  ]
    var collectionView: UICollectionView!

    override func viewDidLoad()
    {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.whiteColor()

        collectionView = UICollectionView(frame: CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y + 20.0, self.view.frame.size.width, self.view.frame.size.height), collectionViewLayout: CustomCollectionViewLayout())

        self.collectionView.delegate = self     // delegate  :  UICollectionViewDelegate
        self.collectionView.dataSource = self   // datasource  : UICollectionViewDataSource

        self.collectionView.registerClass(ColumnCell.self, forCellWithReuseIdentifier: columnCellIdentifier)
        self.collectionView.registerClass(RowCell.self, forCellWithReuseIdentifier: rowCellIdentifier)
        self.collectionView.backgroundColor = UIColor.whiteColor()

        self.view.addSubview(self.collectionView)


    }

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

    // MARK - UICollectionViewDataSource Methods

    // This is number of rows that we want in table
    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
    {
        return 20
    }

    // This is number of columns that we want in table
    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        return 20
    }

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

        if indexPath.section == 0
        {
            if indexPath.row == 0
            {
                let columnCell : ColumnCell = collectionView.dequeueReusableCellWithReuseIdentifier(columnCellIdentifier, forIndexPath: indexPath) as! ColumnCell
                columnCell.columnLabel.text = "⬇️ DATE / NAMES➡️"

                return columnCell

            }
            else
            {
                // First Section -> row data 

                let rowCell : RowCell = collectionView .dequeueReusableCellWithReuseIdentifier(rowCellIdentifier, forIndexPath: indexPath) as! RowCell

                if(indexPath.row < arrayOfData.count)
                {
                    rowCell.textLabel.text = "\(arrayOfData[indexPath.row])"
                }
                else
                {
                    rowCell.textLabel.text = "\([indexPath.section]),\([indexPath.row])"
                }

                if indexPath.section % 2 != 0
                {
                    rowCell.backgroundColor = limeYellowColor
                }
                else
                {
                    rowCell.backgroundColor = UIColor.whiteColor()
                }

                return rowCell
            }
        }
        else
        {
            if indexPath.row == 0
            {
                let columnCell : ColumnCell = collectionView .dequeueReusableCellWithReuseIdentifier(columnCellIdentifier, forIndexPath: indexPath) as! ColumnCell

                columnCell.columnLabel.text = "\(indexPath.section),\(indexPath.row)"

                if indexPath.section % 2 != 0
                {
                      columnCell.backgroundColor = limeYellowColor
                }
                else
                {
                    columnCell.backgroundColor = UIColor.whiteColor()
                }

                return columnCell
            }
            else
            {
                let rowCell : RowCell = collectionView .dequeueReusableCellWithReuseIdentifier(rowCellIdentifier, forIndexPath: indexPath) as! RowCell
               rowCell.textLabel.text = "I am a rockstar yo yo baby"

                if indexPath.section % 2 != 0
                {
                    rowCell.backgroundColor = limeYellowColor
                }
                else
                {
                    rowCell.backgroundColor = UIColor.whiteColor()
                }

                return rowCell
            }
        }
    }

    // MARK - UICollectionViedDelegate Methods

    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
    {
        println(indexPath.section, indexPath.row)
    }

    // MARK - Private Methods

    func getMaxLengthOfTextFromArrayOfData() -> Int
    {
        var maxLength = 0

        for item in self.arrayOfData
        {
            if(maxLength < count(item))
            {
                maxLength = count(item)
            }
        }
        println("MaxLength is \(maxLength)")
        return maxLength
    }



}

UICollectionViewLayout Subclass

class CustomCollectionViewLayout: UICollectionViewLayout
{
    // The following code is only executed the first time we prepare the layout
    var numberOfColumns = 0
    var itemAttributes  : NSMutableArray!
    var itemsSize       : NSMutableArray!
    var contentSize     : CGSize!
    var dataForItems    : NSMutableArray!



    override func prepareLayout()
    {

        println("prepareLayout")
        if self.collectionView?.numberOfSections() == 0
        {
            return
        }

        var tempVar = self.collectionView?.numberOfItemsInSection(0)
        numberOfColumns = tempVar!


        // Creating layout attributes of each item by looping though the numberOfItems

        if (self.itemAttributes != nil && self.itemAttributes.count > 0)
        {
            for section in 0..<self.collectionView!.numberOfSections()
            {
                var numberOfItems : Int = self.collectionView!.numberOfItemsInSection(section)

                for index in 0..<numberOfItems
                {
                    if section != 0 && index != 0
                    {
                        continue
                    }

                    var attributes : UICollectionViewLayoutAttributes = self.layoutAttributesForItemAtIndexPath(NSIndexPath(forItem: index, inSection: section))

                }
            }
            return
        }

        // Calculating the item Size
        if (self.itemsSize == nil || self.itemsSize.count != numberOfColumns)
        {
            self.calculateItemsSize()
        }

        var column = 0

        var xOffset : CGFloat = 0
        var yOffset : CGFloat = 0

        var contentWidth  : CGFloat = 0
        var contentHeight : CGFloat = 0

        // We loop through all items
        for section in 0..<self.collectionView!.numberOfSections()
        {
            var sectionAttributes = NSMutableArray()

            for index in 0..<numberOfColumns
            {
                println("ItemSize - \(self.itemsSize)")
                var itemSize = self.itemsSize[index].CGSizeValue()
                var indexPath = NSIndexPath(forItem: index, inSection: section)
                var attributes = UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
                attributes.frame = CGRectIntegral(CGRectMake(xOffset, yOffset, itemSize.width, itemSize.height))

                // setting the zIndex of attributes
                if section == 0 && index == 0
                {
                    attributes.zIndex = 1024;
                }
                else  if section == 0 || index == 0
                {
                    attributes.zIndex = 1023
                }

                if section == 0
                {
                    var frame = attributes.frame
                    frame.origin.y = self.collectionView!.contentOffset.y
                    attributes.frame = frame
                }
                if index == 0
                {
                    var frame = attributes.frame
                    frame.origin.x = self.collectionView!.contentOffset.x
                    attributes.frame = frame
                }

                sectionAttributes.addObject(attributes)

                xOffset += itemSize.width
                column++

                if column == numberOfColumns
                {
                    if xOffset > contentWidth
                    {
                        contentWidth = xOffset
                    }

                    column = 0
                    xOffset = 0
                    yOffset += itemSize.height
                }
            }
            if (self.itemAttributes == nil)
            {
                self.itemAttributes = NSMutableArray(capacity: self.collectionView!.numberOfSections())
            }
            self.itemAttributes .addObject(sectionAttributes)
        }

        var attributes : UICollectionViewLayoutAttributes = self.itemAttributes.lastObject?.lastObject as! UICollectionViewLayoutAttributes
        contentHeight = attributes.frame.origin.y + attributes.frame.size.height

        self.contentSize = CGSizeMake(contentWidth, contentHeight)
    }

    override func collectionViewContentSize() -> CGSize
    {
        println("collectionViewContentSize")
        return self.contentSize
    }

    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes!
    {
        println("layoutAttributesForItemAtIndexPath")
        return self.itemAttributes[indexPath.section][indexPath.row] as! UICollectionViewLayoutAttributes
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]?
    {
         println("layoutAttributesForElementsInRect")
        var attributes : NSMutableArray = NSMutableArray()

        for section in self.itemAttributes
        {
            attributes.addObjectsFromArray(
                section.filteredArrayUsingPredicate(
                    NSPredicate(block: { (evaluatedObject, bindings) -> Bool in

                        return CGRectIntersectsRect(rect, evaluatedObject.frame)
                    })
                )
            )
        }
        return attributes as [AnyObject]
    }

    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool
    {
        println("shouldInvalidateLayoutForBoundsChange")
        return true
    }

    // MARK : Private Methods

    func sizeForItemWithColumnIndex(columnIndex: Int) -> CGSize
    {
        println("sizeForItemWithColumnIndex")
        var text : String = ""
        switch (columnIndex)
        {

        case 0:
            text = "Col 0"
        case 1:
            text = "Col 1"
        case 2:
            text = "Col 2"
        case 3:
            text = "Col 3"
        case 4:
            text = "Col 4"
        case 5:
            text = "Col 5"
        case 6:
            text = "Col 6"
        default:
            text = "Col 7"

     // HOW DO I GET THE MAX SIZE OF COLUMN CELLs HERE, SO THAT I CAN FIX THE WIDTH OF THE ENTIRE COLUMN?       
        }

        var size : CGSize = (text as NSString).sizeWithAttributes([NSFontAttributeName: UIFont.systemFontOfSize(17.0)])
        let width : CGFloat = size.width + 25

        println("ItemSize - \(self.itemsSize)")
        return CGSizeMake(width, 40)
    }

    func calculateItemsSize()
    {
         println("calculateItemsSize")
        self.itemsSize = NSMutableArray(capacity: numberOfColumns)

        // Storing the calculated sizes in itemsSize array in order to do the calculations only once per column.
        for index in 0..<numberOfColumns
        {
            self.itemsSize.addObject(NSValue(CGSize: self.sizeForItemWithColumnIndex(index)))

        }
    }


}

- UICollectionViewCell (ColumnCell) UILabel ?

+4
1

. , invalidateLayout, .

+2

Source: https://habr.com/ru/post/1589151/


All Articles