Rahul Mandalkar
Rahul Mandalkar

Reputation: 87

How to set Custom layout for UICollectionView in Ios

I know how to display Collection cell in an UICollectionView. But I want to use a custom layout for displaying the cells.

My Collection should have two column where the width of cells is fixed, but cell's height may vary depending on label's content size.

But I don't know how to do it. I want to display the cell collection as shown in the picture below

Layout Example

I'd like to implement it in swift.

Upvotes: 3

Views: 8561

Answers (3)

Shubham Daramwar
Shubham Daramwar

Reputation: 287

Following is the code for custom layout. In this layout cell width is half of the collection view and the height is specific for each cell. To set height for each cell you have to implement "func heightFor(index : Int) -> CGFloat". To implement this layout do following steps.

  1. Create following protocol:

    //This protocol has to be confirmed in your controller.
    
      protocol CustomLayoutDelegate {
          //This method accept the height for your cell.
          func heightFor(index : Int) -> CGFloat
       }
    
  2. Create subclass "UICollectionViewFlowLayout":

    class CustomLayout: UICollectionViewFlowLayout {
       //Variables
       var layoutDelegate : CustomLayoutDelegate?
       var leftY = CGFloat(0)
       var rightY = CGFloat(0)
       var cache = [UICollectionViewLayoutAttributes]()
    
    //This method sets the frame (attributes) for every cell and store in chache.
    override func prepare() {
        guard  cache.isEmpty == true, let collectionView = collectionView else{
            return
        }
        let verticalSpacing = CGFloat(10)
        let horizontalSpacing = CGFloat(10)
        let margin = CGFloat(10)
        leftY = margin
        rightY = margin
        for item in 0..<collectionView.numberOfItems(inSection: 0){
            var frame = CGRect.zero
            let cellHeight = self.layoutDelegate!.heightFor(index: item)
            frame.size.height = cellHeight
            frame.size.width = (collectionView.frame.size.width - 2 * margin) / 2 - horizontalSpacing/2
            if item%2 == 0{
                frame.origin.x = margin
                frame.origin.y = leftY
                leftY += cellHeight + verticalSpacing
            }else{
                frame.origin.x = (collectionView.frame.size.width - 2 * margin) / 2 +  ((4 * horizontalSpacing) / 3)
                frame.origin.y = rightY
                rightY += cellHeight + verticalSpacing
            }
    
            let indexPath = IndexPath(item: item, section: 0)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = frame
            cache.append(attributes)
        }
    
    }
    
    // This method returns the array of
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
        // Loop through the cache and look for items in the rect
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                visibleLayoutAttributes.append(attributes)
            }
        }
        return visibleLayoutAttributes
    }
    
    //This method sets the collection view's content size.
    override var collectionViewContentSize: CGSize{
        return CGSize.init(width: collectionView!.frame.size.width, height: max(leftY, rightY))
    }
    

    }

  3. Now, in your view controller write the following code:

    import UIKit class ViewController: UIViewController,UICollectionViewDataSource, CustomLayoutDelegate {

    @IBOutlet weak var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        //Register the cell for collection view
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    
        //Assign the delegate
        if let layout = collectionView.collectionViewLayout as? CustomLayout{
            layout.layoutDelegate = self
        }
    }
    
    //CustomLayoutDelegate method
    func heightFor(index: Int) -> CGFloat {
    
        //Implement your own logic to return the height for specific cell
        return CGFloat(max(1, index) * 50)
    }
    
    // UICollectionViewDataSource methods
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
        cell.backgroundColor = UIColor.red
        return cell
    }
    }
    
  4. Now on your xib, Add collection view and connect to

    @IBOutlate weak var collectionView: UICollectionView!

    and assign our "CustomLayout" class to collection's layout:

Assign "CustomLayout" class to collection's layout

Now, after running the project you will see the following output. Make sure collection view cell has different color, just for identification.

Final output

Upvotes: 8

benoitcn
benoitcn

Reputation: 159

  1. Customize a UICollectionViewLayout class
  2. Define a protocol for your view controller to calculate every cell's height
  3. Make your view controller as the delegate
  4. In the delegate methods, you need to figure out the dynamic height of each cell.
  5. Override prepare function, which will invoke the delegate methods for every cell's height and generate a custom UICollectionViewLayoutAttributes instance for each cell, in the layout subclass.
  6. You may also need to store these UICollectionViewLayoutAttributes instances in an array for better performance to avoid the recalculation. (This step is useful only when the UICollectionView prefetching is disabled.)
  7. Override layoutAttributesForElements function to return the layout attributes for all the items inside the given rectangle.
  8. Apply those UICollectionViewLayoutAttributes instances in your customized UICollectionViewCell in order to do some auto layout.
  9. Override copy and isEqual function in your UICollectionViewLayoutAttributes subclass. Subclasses of UICollectionViewLayoutAttributes need to conform to the NSCopying protocol because the attribute’s objects can be copied internally. You must implement isEqual to compare the custom properties of your subclass, such as the height of the label.

Upvotes: 0

Ayush sharma
Ayush sharma

Reputation: 169

Using this function you can change the height and width of cell as you want.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize{}

use the indexPath to access the cell

Upvotes: 0

Related Questions