15

I have a collection view and I would like to have let's say 4 cells per row. I know that to accomplish this all I need to do is divide collectionview.frame.size.width by 4. This is easy. However, what I can not figure out, is how to take into consideration the insets at the side of the collection view and the spacing between the cells. I have 10 pixel insets on the left and right of the collection view, as well as there is a 10 pixel spacing between the cells. How can I calculate the required cell width taking these 10 px insets into account?

Balázs Vincze
  • 3,550
  • 5
  • 29
  • 60
  • You can see my answer here http://stackoverflow.com/questions/14674986/uicollectionview-set-number-of-columns/37697658#37697658 – HotJard Jun 08 '16 at 08:53

6 Answers6

41

Swift 4+

UICollectionViewDataSource method

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

    let noOfCellsInRow = 4

    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout

    let totalSpace = flowLayout.sectionInset.left
        + flowLayout.sectionInset.right
        + (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))

    let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))

    return CGSize(width: size, height: size)
}
Naveed Ahmad
  • 6,627
  • 2
  • 58
  • 83
Gagandeep Gambhir
  • 4,225
  • 1
  • 29
  • 34
26

iOS 13 Swift 5+

Collectionview Estimate Size must be none

enter image description here

Declare margin for cell

let margin: CGFloat = 10

In viewDidLoad configure minimumInteritemSpacing, minimumLineSpacing, sectionInset

 guard let collectionView = yourCollectionView, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return }

    flowLayout.minimumInteritemSpacing = margin
    flowLayout.minimumLineSpacing = margin
    flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)

UICollectionViewDataSource method sizeForItemAt

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

    let noOfCellsInRow = 2   //number of column you want
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let totalSpace = flowLayout.sectionInset.left
        + flowLayout.sectionInset.right
        + (flowLayout.minimumInteritemSpacing * CGFloat(noOfCellsInRow - 1))

    let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(noOfCellsInRow))
    return CGSize(width: size, height: size)
}
Sandeep Maurya
  • 1,979
  • 17
  • 22
8

Swift 3.0. Works for both horizontal and vertical scroll directions and variable spacing

Declare number of cells you want per row

let numberOfCellsPerRow: CGFloat = 4

Configure flowLayout to render specified numberOfCellsPerRow

if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
    let horizontalSpacing = flowLayout.scrollDirection == .vertical ? flowLayout.minimumInteritemSpacing : flowLayout.minimumLineSpacing
    let cellWidth = (view.frame.width - max(0, numberOfCellsPerRow - 1)*horizontalSpacing)/numberOfCellsPerRow
    flowLayout.itemSize = CGSize(width: cellWidth, height: cellWidth)
}
Jože Ws
  • 1,754
  • 17
  • 12
7

To better explain the math, faarwa's example is only for 4 cells. Assuming 1 row, there are 5 blocks of spacing for 4 cell items (stick out 4 fingers and count the spaces from far end of pinky to far end of index finger).

There will always be n + 1 spaces for the n cells you have in one row. Faarwa assumes each space is 10 so he multiplied 5 cells by 10 to get 50. You need to figure out how much space you have left to work with after padding— so if assuming your screen width is 200, you must subtract the two values to get the remaining width, or "(collectionView.frame.size.width-50)".

Continuing with the 200 width assumption and 4 cell items, you must divide your difference (150) by 4 to get the width each cell should be for equal spacing.

Experiment and go through some trial and error, but here are the 2 methods I used to get a collection view of exercise sets from an array of set objects.

// UICollectionViewDataSource method

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

    let numberOfSets = CGFloat(self.currentExSets.count)

    let width = (collectionView.frame.size.width - (numberOfSets * view.frame.size.width / 15))/numberOfSets

    let height = collectionView.frame.size.height / 2

    return CGSizeMake(width, height);
}

// UICollectionViewDelegateFlowLayout method

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout,
insetForSectionAtIndex section: Int) -> UIEdgeInsets {

    let cellWidthPadding = collectionView.frame.size.width / 30
    let cellHeightPadding = collectionView.frame.size.height / 4
    return UIEdgeInsets(top: cellHeightPadding,left: cellWidthPadding, bottom: cellHeightPadding,right: cellWidthPadding)
}

SCREENSHOT:

enter image description heretwo cell items

four cell items enter image description here

Community
  • 1
  • 1
Leo
  • 311
  • 3
  • 5
0
(collectionView.frame.size.width-50)/4

To account for the space, you can subtract it from the collection view frame width (subtracting 5*10).

faarwa
  • 146
  • 4
  • Yes, (I thought) I had literally the exact same code and it did not work. It was driving me crazy...cmon I can not suck in maths this much.. Turns out, i had written view.frame.size.width instead of collection view.. Thanks again! – Balázs Vincze Feb 08 '16 at 23:52
  • 1
    Please add an explanation as to why this works, or what it is doing so that others can benefit. – CaptJak Feb 08 '16 at 23:57
0

this function to set no. of cells per row in collection view

func setcollectionViewFlowlayout(){
       let numberofCells = 5
       let width = (emojiCollectionView.bounds.size.width - CGFloat((numberofCells - 1) * 10)) / CGFloat(numberofCells)
       let layout = emojiCollectionView.collectionViewLayout as! UICollectionViewFlowLayout
       layout.scrollDirection = .vertical // .horizontal
       layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
       layout.minimumLineSpacing = 1
       layout.minimumInteritemSpacing = 1
       layout.itemSize = CGSize(width: width, height: width)
       emojiCollectionView.setCollectionViewLayout(layout, animated: true)
       emojiCollectionView.reloadData()
    }