john doe
john doe

Reputation: 9660

CollectionView sizeForItemAtIndexPath never called

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) 
}

Upvotes: 117

Views: 71963

Answers (15)

Abhijeet Ravi
Abhijeet Ravi

Reputation: 295

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)
    }
}

Upvotes: -1

Shiv Kumar
Shiv Kumar

Reputation: 942

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

Upvotes: 66

H S W
H S W

Reputation: 7129

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.

Upvotes: 13

Sébastien
Sébastien

Reputation: 14831

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).

Upvotes: -1

烛龙一现
烛龙一现

Reputation: 29

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

Upvotes: 0

Muzammil
Muzammil

Reputation: 1539

Just add: UICollectionViewDelegateFlowLayout

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

Upvotes: 0

Xys
Xys

Reputation: 10839

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)
} 

Upvotes: 35

Manish
Manish

Reputation: 722

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.

Upvotes: 8

Zenman C
Zenman C

Reputation: 1723

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

Upvotes: 78

Stephen Rains
Stephen Rains

Reputation: 39

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)
    }
}

Upvotes: 2

Azam
Azam

Reputation: 797

Make sure you put this in viewDidLoad()

collectionView.delegate = self

Upvotes: 2

Charlie S
Charlie S

Reputation: 4594

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 :-(

Upvotes: -2

Oliver Eichhorn
Oliver Eichhorn

Reputation: 307

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.

Upvotes: -14

Gaurav Rami
Gaurav Rami

Reputation: 1287

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 {

}

Upvotes: 17

almas
almas

Reputation: 7187

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

class PhrasesCompactCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout

Upvotes: 282

Related Questions