Search code examples
iosswiftuicollectionviewautolayout

N number of Collection View cells - autoresize to fir all cells in screen


I need to fit my collection view cells inside the iphone screen bounds always.

It should work like :

  • If one cell, it should fill entire screen
  • If two cells, first cell should fill the first half and second should fill second half
  • if three cells, screen should divide in 4 equal cells and 4th cell placeholder should be empty. In case of 4th cell, it should fill the empty space.
  • If five/six cells, there should be three rows of two cells each.

I had a logic but with some hack I managed to support only 6 cells, after that it doesn't work.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        
        var horizontalTiles = 1
        var verticalTiles = 1
        
        var targetWidth = collectionView.frame.width
        var targetHeight = collectionView.frame.height - UIApplication.shared.statusBarFrame.height
        
        horizontalTiles = horizontalTiles+(self.contactsArray.count/3)
        verticalTiles = verticalTiles+(self.contactsArray.count/2)
        
        if self.contactsArray.count%4 == 0 || self.contactsArray.count%6 == 0 {
            return lastCellSize
        }
        
        targetWidth = targetWidth / CGFloat(horizontalTiles)
        targetHeight = targetHeight / CGFloat(verticalTiles)
        
        let size = CGSize(width: targetWidth, height: targetHeight)
        
        lastCellSize = size
        
        return size
    }

If there is any better way or by using flow layout it would be very helpful.

enter image description here


Solution

  • Setting the animations aside... you could get the sizes with some clever math/logic (your question asks specifically about sizing).

    Some observations/assumptions:

    • All cells are the same size, so we can return one size
    • Number of rows/columns alternate incrementing
    • Rows increment before columns
    • Size remains the same until all columns are filled in the last row

    Given this...

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
        let numItems = self.contactsArray.count
    
        let targetWidth = collectionView.frame.width
        let targetHeight = collectionView.frame.height - UIApplication.shared.statusBarFrame.height
    
        if numItems == 1 {
            return CGSize(width: targetWidth, height: targetHeight)
        }
        
        // Below logic only works for numItems > 1, so we check for that ^
    
        var numRows = 1
        var numColumns = 1
    
        for index in 1...(numItems-1) {
            if index % numColumns == 0 {
                if numColumns == numRows {
                    numRows += 1
                } else {
                    numColumns += 1
                }
            }
        }
    
        return CGSize(width: targetWidth / numColumns, height: targetHeight / numRows)
    }