117

This is driving me crazy! I have a UICollectionViewController as shown below:

class PhrasesCompactCollectionViewController: UICollectionViewController

The numberOfSections and cellForItemAt are being called but the sizeForItemAtIndexPath is never called. I am using the same exact code somewhere else and it fires correctly. I am using Xcode 8 Beta 6.

func collectionView(collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAtIndexPath indexPath:  NSIndexPath) -> CGSize {
    return CGSize(width: 120, height:120) 
}

15 Answers 15

282

You need to specify that you implement the protocol UICollectionViewDelegateFlowLayout in your class declaration.

class PhrasesCompactCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout

2
  • 13
    I did this but it's still not called. :( Commented Oct 23, 2017 at 18:39
  • 1
    Annoying how I don't get an error while trying to use sizeForItemAt without the flow layout delegate. Just wasted 2 hours debugging :/ Commented Feb 14, 2021 at 20:44
78

I had the same problem. The one thing that helped me was to make the 'Estimated Size' for the Cell Size under the collectionView size inspector 'None'.

enter image description here

5
  • 1
    Somehow all other answers on all other posts about using sizeForItemAt failed to mention this. Very useful.
    – JCutting8
    Commented Feb 1, 2020 at 13:13
  • This is crazy!!!! I tested on iPhone 5 simulator with 10.3.1 iOS version and the method get called. on iPhone 5s simulator with 10.3.1 version method does not get called!!! I changed Estimate Size to None. It started to get called on iPhone 5s. WT WT WT
    – hasan
    Commented Mar 11, 2020 at 18:40
  • Thanks a lot! My collection view with custom sizeForItemAt worked well until Xcode 11.4 when suddenly the cells were wrong width. Tried bunch of things and this finally worked.
    – Filip
    Commented Apr 20, 2020 at 13:56
  • Thanks! It's both frustrating and infuriating that Apple made little things like this extremely complicated, and we all have to discuss about it here.
    – KMW
    Commented Sep 9, 2020 at 9:03
  • How do you set that without storyboards?
    – henrique
    Commented Sep 16, 2022 at 15:18
66

In Swift 3+ Use this method:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width:(collectionView.frame.height-90)/2, height: 100)
}

If You are using UICollectionView then make sure that your class complies both Delegate

  • UICollectionViewDelegate

  • UICollectionViewDelegateFlowLayout

4
  • 5
    It is redundant to comply to UICollectionViewDelegate as UICollectionViewController already does. Commented Oct 23, 2017 at 18:38
  • 4
    @VanDuTran It's not redundant to state that it's required because being a UICollectionViewController isn't required... Commented Feb 26, 2018 at 9:48
  • I think you meant that a non collection view, Viewcontroller can be the UICollectionViewDelegate. Commented Oct 15, 2018 at 6:40
  • 1
    UICollectionViewDelegateFlowLayout inherit from UICollectionViewDelegate so just implement the UICollectionViewDelegateFlowLayout
    – evya
    Commented Jan 9, 2019 at 9:26
35

Swift 5

All these steps are required :

  1. Conform to UICollectionViewDelegateFlowLayout <== Absolutely needed !!
  2. and to UICollectionViewDelegate, UICollectionViewDataSource
  3. Set these in viewDidLoad : collectionView.delegate = self and collectionView.dataSource = self
  4. Set Estimate Size to None in the Interface Builder
  5. Make sure the following method has sizeForItemAt and not sizeForItemAtIndexPath

as :

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
    return CGSize(width: 100, height:100)
} 
2
  • 8
    Estimate Size to None - save my day! Commented Jun 11, 2020 at 12:25
  • 1
    How do you set Estimate size to None programmatically if you're not using IB?
    – henrique
    Commented Sep 16, 2022 at 15:19
17

Swift 3

To set Cell Size of UICollectionView, you need to create and customise object of UICollectionViewFlowLayout. Customise the properties and set that layout object to the UICollectionView object.

Change cellheight and cell cellWidth objects according to your requirement (the cell hight and width you want).

override func viewDidLoad() {
    super.viewDidLoad()

    let cellWidth : CGFloat = myCollectionView.frame.size.width / 4.0
    let cellheight : CGFloat = myCollectionView.frame.size.height - 2.0
    let cellSize = CGSize(width: cellWidth , height:cellheight)

    let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .vertical //.horizontal
    layout.itemSize = cellSize
    layout.sectionInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
    layout.minimumLineSpacing = 1.0
    layout.minimumInteritemSpacing = 1.0
    myCollectionView.setCollectionViewLayout(layout, animated: true)

    myCollectionView.reloadData()
}

Make Sure: if you added sizeForItemAt indexPath method then must REMOVE the method...

Remove Below Method

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

}
5
  • 12
    How is this helpful?
    – almas
    Commented Apr 3, 2017 at 18:14
  • 1
    Almas, This will helpful for set custom cell size of UICollectionViewCell... There are variables cellWidth and cellHeight, in that you can define your own values... As well as "layout.sectionInset", "layout.minimumLineSpacing" and "layout.minimumInteritemSpacing", these variables also matters for UICollectionViewCell size.. Commented Apr 4, 2017 at 6:09
  • 2
    This only works if all cells are the same size. The below answer is the correct answer
    – JeffN
    Commented Jun 11, 2017 at 19:23
  • this is for changing size of the UICollectionView but not the cell inside the view
    – Async-
    Commented Jul 5, 2017 at 11:13
  • This is not an answer to why the delegate method is not called
    – Ziv Kesten
    Commented Oct 6, 2019 at 11:53
13

First class need to confirm to UICollectionViewDelegateFlowLayout. Then you need to write following code in viewDidLoad():

//To tell the UICollectionView to use your UIViewController's UICollectionViewDelegateFlowLayout methods

collectionView.delegate = self

// If you want to set your own collection view flow layout

let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical //depending upon direction of collection view

self.collectionView?.setCollectionViewLayout(layout, animated: true)

By using this code UICollectionViewDelegateFlowLayout method

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

will get called, you can set size in this method.

0
8

Three things you need to check if no answer works:

  1. Implement FlowLayout Delegate : class MyController: UICollectionViewController, UICollectionViewDelegateFlowLayout

  2. Use method for size of item for swift 3.0+: func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: (collectionView.frame.width / 3) - 1, height: collectionView.frame.width) }

  3. Select Estimate size = None in Inspect element of UICollectionView.

1
  • 1
    How do you set Estimate size to None programmatically if you're not using IB?
    – henrique
    Commented Sep 16, 2022 at 15:20
2

Make sure you put this in viewDidLoad()

collectionView.delegate = self
2

I had the same problem and nothing would fix it. I had a yellow warning saying to make it private because it came close to another function defined by the Layout Delegate. What fixed it for me was:

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

Notice the difference from "sizeForItemAtIndexPath" to the correct "sizeForItemAt".

I tore my hair out for days trying to figure this out and it finally worked. Below I will include all of my function to show you how I also "hid" a cell by making the height equal to 0.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let findIndex = labelArray.index(of: "\(labelArray[indexPath.row])")
    let screenSize = UIScreen.main.bounds
    let screenWidth = screenSize.width

    if (findIndex! % 2 == 0){
        // Even index, show cell
        return CGSize(width: screenWidth, height: 246)
    }
    else {
        return CGSize(width: screenWidth, height: 0)
    }
}
0

Just add: UICollectionViewDelegateFlowLayout

class YourClass: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {}
0

may you set the wrong layout: UICollectionViewLayout, actually this layout should be UICollectionViewFlowLayout this like

-1

I managed to fix the same issue by calling

collectionView.delegate = self

BEFORE

collectionView.dataSource = self

I have static items and never reload the data. Apparently setting the dataSource triggers the loading of the collection data synchronously, so if the delegate is not set at this point, then sizeForItemAtIndexPath never gets called (unless reloadData is called later).

-1

Don't forget to use UICollectionViewDelegateFlowLayout. Set the spacings in xib/storyboard and resize the cell with below-updated function for newer Swift versions. Happy coding !!

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

    let numberOfItemsPerRow:CGFloat = 5
    
    if let collection = self.collectionView {
        let width = collection.bounds.width / numberOfItemsPerRow
        
        return CGSize(width: width, height: 36)
    } else {
        return CGSize(width: 0, height: 0)
    }
}
-2

I had this issue last night. Finally solved it at midnight when I realised 'didSelectItemAtIndex' method was not closed off with a closure bracket "}"

I added a closure bracket at the very bottom of the class when asked by the compile error. So in effect all the methods below 'didSelectItemAtIndex' were inside that method.

Posting here just in case anyone else wastes 4 hours of their evenings on this one :-(

-14

For me (in Swift 3.1) the method must be declared public like this:

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

Don't forget to make it public.

1
  • 1
    public is what you use to make your code accessible between frameworks and modules. Commented Oct 25, 2018 at 22:11

Not the answer you're looking for? Browse other questions tagged or ask your own question.