110

Only my second time using UICollectionView's and perhaps I have bitten off more than I can chew but nevertheless:

I am implementing a UICollectionView (myCollectionView) that uses custom UICollectionViewCell's that I have subclassed. The subclassed cells (FullReceiptCell) contain UITableView's and are the size of the viewcontroller. I am trying to allow for horizontal scrolling between the FullReceiptCells.

The subclassed UICollectionViewController that contains myCollectionView is being pushed on to a nav controller stack. Currently, myCollectionView loas and horizontal scrolling is enabled. However, no cells are visible. I have confirmed that

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

has run and is returning an integer greater than 0. I have also confirmed that myCollectionView's delegate and datasource are properly set in IB to the subclassed UICollectionViewController.

The method where the cells are to be loaded:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

is not being called.

Here is where I push the UICollectionViewController and my viewDidLoad method within that controller (NOTE: initWithBill is an override of the normal initializer):

In the prior ViewControllers .m file:

FullReceiptViewController *test = [[FullReceiptViewController alloc] initWithBill:currentBill];
test.title = @"Review";
[self.navigationController pushViewController:test animated:YES];

In FullReceiptViewController.m:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [self.myCollectionView registerClass:[FullReceiptCell class] forCellWithReuseIdentifier:@"FullReceiptCellIdentifier"];
    self.myCollectionView.pagingEnabled = YES;
    // Setup flowlayout

    self.myCollectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
    [self.myCollectionViewFlowLayout setItemSize:CGSizeMake(320, 548)];
    [self.myCollectionViewFlowLayout setSectionInset:UIEdgeInsetsMake(0, 0, 0, 0)];
    [self.myCollectionViewFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
    self.myCollectionViewFlowLayout.minimumLineSpacing = 0;
    self.myCollectionViewFlowLayout.minimumInteritemSpacing = 0;
    [self.myCollectionView setCollectionViewLayout:myCollectionViewFlowLayout];
    //testing to see if the collection view is loading
    self.myCollectionView.backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.0f];

Any clue as to why it is not being called?

sunkehappy
  • 8,565
  • 5
  • 36
  • 60
IkegawaTaro
  • 3,273
  • 5
  • 19
  • 25

31 Answers31

140

For those who stumble here later.... the reason:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

was not being called was because of the itemSize for the collectionViewFlowLayout's height was too big.

[self.myCollectionViewFlowLayout setItemSize:CGSizeMake(320, 548)];

If I change the height to 410, it will execute cellForItemAtIndexPath.

IkegawaTaro
  • 3,273
  • 5
  • 19
  • 25
100

In my case, it was because my layout class incorrectly subclassed from UICollectionViewLayout instead of UICollectionViewFlowLayout

esilver
  • 25,374
  • 21
  • 112
  • 159
33

The cellForItemAtIndexPath will not get called if you do not provide the content size information to collection view.

  1. If you are using Flow layout: You need to set the item sizes properly.
  2. If you have a custom layout subclassed from UICollectionViewLayout: Ensure you are returning a proper size from the collectionViewContentSize method.

In case of the latter, you will also observe that your layoutAttributesForElementsRect is also not called. The reason is that you have not specified what is your content size and by default the size will be CGSizeZero. This basically tell collection view that you don't have any content to paint so it does not bother asking you for attributes or cells.

So, just override collectionViewContentSize and provide a proper size there, it should solve your problem.

Deepak G M
  • 1,593
  • 16
  • 12
  • 1
    For example to use the full available size, return collectionView.frame.size from collectionViewContentSize (not simply collectionView.contentSize which defaults to 0, 0) – Adam Loving Apr 14 '15 at 17:44
  • Thanks! This answer helped me b/c I have a custom UICollectionViewLayout. If I started up the app in simulator in landscape orientation, some cells were not appearing in the initial view. Overriding the content size fixed this problem for me. – Chris Livdahl Apr 12 '17 at 05:24
  • 1
    Thank you. In my case the `collectionViewContentSize` incorrectly returned 0 for the width, that's why the `cellForItemAtIndexPath` was not getting called. – izerik Mar 22 '18 at 13:52
17

For a more complicated view hierachy please check this blog. It saved my life!

self.automaticallyAdjustsScrollViewInsets = NO;
Jaybo
  • 836
  • 10
  • 11
  • 1
    I spent so much time on this - and it all came down to that. If using storyboard or XIB, look at the parent view controller and deselect "Adjust Scroll View Insets" in the Attribute Inspector which is turned on by default. I tried all the other answers and they didn't work, it came down to this. – etayluz Mar 29 '16 at 21:03
16

Maybe I'm just overlooking it, but it appears your missing your delegate and data source. In your header file, make sure you have added these:

<UICollectionViewDelegate, UICollectionViewDataSource>

and in your viewDidLoad method add this:

self.myCollectionView.delegate = self;
self.myCollectionView.dataSource = self;

Also, if you are loading it via an .xib, make sure you are have connected the IBOutlet to the UICollectionView.

Anthony Castelli
  • 557
  • 5
  • 17
  • 1
    I didn't show it but I already have the delegate and datasource noted in the header file. Additionally, I have wired the IBOutlet to myCollectionView. The delegate and datasource for this collectionview have been set to FullReceiptViewController. The strange thing is that numberOfItemsInSection (a datasource protocol method) is getting called and returning an integer greater than 0. – IkegawaTaro Feb 03 '13 at 08:31
  • Hmm, When you launch the application does the Collection view show or do anything? It sounds like it does have some items in it if numberOfItemsInSection returns something greater than 0. – Anthony Castelli Feb 04 '13 at 00:46
  • Thanks for following up. The collectionview does show... I can tell because I changed the background color. I believe I have found the problem: [self.myCollectionViewFlowLayout setItemSize:CGSizeMake(320, 548)]; This was too big. If I decrease the height to 410, I can get the cellForItemAtIndexPath method to be called. – IkegawaTaro Feb 04 '13 at 07:13
  • @AnthonyCastelli Hello,I am also facing the same issue, numberOfItemsInSection being called on pop to viewController but not cellForItemsAtIndexPath. so do you get the clue? – Bhavin Kansagara Nov 24 '13 at 08:01
15

If your class is subclass from UICollectionviewController and you are creating collectionView programmatically then use

let layout = UICollectionViewFlowLayout()
    layout.scrollDirection = .Vertical
    layout.itemSize = CGSizeMake(50, 50)

    collectionView?.setCollectionViewLayout(layout, animated: false)
Gurjinder Singh
  • 5,576
  • 44
  • 41
  • This is the answer solved my problem! The override method func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { } does not work for me. – Mike Feb 23 '19 at 19:15
10

I was using autolayout, and in my case height constraint was missing

AamirR
  • 9,385
  • 3
  • 47
  • 59
6

My issue was that I had 2 collection views within the same view, and both were sharing a computed UICollectionViewFlowLayout instance. Once I created separate instances of the same flow layout, it worked fine.

nebyark
  • 101
  • 2
  • 5
5

In my case, what I had very stupidly forgotten to do was implement the UICollectionViewDataSource protocol method numberOfItemsInSection, so that is another thing to check.

Marius
  • 14,888
  • 9
  • 50
  • 73
Dean
  • 71
  • 1
  • 2
5

Also, make sure that reloadData is not called while collection view is updating with animation (insert, delete, update or performBatchUpdates).

sasha_nec
  • 532
  • 5
  • 12
4

In my case, set collectionView.prefetchingEnabled = NO; solved my problem. Only works on iOS 10.

6david9
  • 767
  • 4
  • 16
  • 2
    I upvoted that answer some time ago and today it helped me again :D You deserve a Noble for this answer man. Thanks again! – Maciek Czarnik Apr 23 '20 at 09:36
  • this answer worked for me! I wish there was an explanation as to why included with it because I never had it set to true to begin with. – Lance Samaria Jun 07 '20 at 22:48
3

It was happening for me when item height == 0 by fixing that cellForItem method was called.

Jirune
  • 2,180
  • 3
  • 20
  • 19
3

Ten minutes ago I also encountered this problem, I made a silly mistake.

My intention is to use UICollectionViewFlowLayout,

But because of mistakes, I used UICollectionViewLayout.

Vincent Sit
  • 1,675
  • 1
  • 16
  • 23
3

In my case, changing UINavigationBar's translucent to NO solved the problem.

chanil
  • 241
  • 1
  • 10
3

In Xcode 7.0.1, we got this problem when we copied a storyboard and accompanying code from another project. The collection view had a custom flow layout set in the storyboard. Solution was to:

  • Remove the flow layout in IB
  • Compile/run the app
  • Set the flow layout back

Now it worked :)

Bart van Kuik
  • 4,082
  • 1
  • 28
  • 50
3

I forgot to set the autoresizing mask to false on the collection view:

collectionView.translatesAutoresizingMaskIntoConstraints = false
Undo
  • 25,204
  • 37
  • 102
  • 124
Stritt
  • 348
  • 2
  • 6
3

In storyboard/xib UICollectionView property cellSize MUST not be {0, 0}

poGUIst
  • 310
  • 4
  • 10
3

In my case I had the collectionView in a UIStackView with alignment = .center. So the collectionView did not have a width. When setting the stackView.alignment to .fill everything was fine.

Nico S.
  • 2,023
  • 1
  • 22
  • 49
2

Make sure numberOfSectionsInCollectionView returns a positive integer as well.

There has to be at least one section in the collection.

Eran Goldin
  • 937
  • 12
  • 21
2

Make sure -(NSInteger) collectionView:numberOfItemsInSection: is returning a positive (read, greater than zero) value. It may be a matter of placing [self.collectionView reloadData]; in the completion handler.

RT Denver
  • 51
  • 3
2

If you're using collection view protocols you must connect the CollectionViewDataSource and CollectionViewDelegate protocols to your ViewController. Interface builder outlets

Marcus Adams
  • 49,523
  • 8
  • 81
  • 132
Declan McKenna
  • 3,803
  • 6
  • 39
  • 67
2

It can be realated layout problems. Check layout warnings from console, if exist.

erdikanik
  • 484
  • 6
  • 10
2

In my case the collection view was not fully visible in its parent view due to wrong auto layout constraints. So fixing the constraints made the collection view all visible and cellForItemAtIndexPath was called.

Khaled Annajar
  • 12,558
  • 4
  • 30
  • 43
1

Same happened to me. I had 2 UICollectionViews and I removed once since I didn't need that. After that I realised that the CellForItemAtIndexPath was not getting called but the other required methods. I tried all of the above but then I did the standard magic. Removed the collection view from storyboard and added again. Then it started working. Not sure why and I have no explanation but maybe some connection issues.

Hafeez
  • 403
  • 4
  • 7
  • I copied my UICollectionView from another UIViewController in storyboard. I deleted the the CollectionView and added again to make it work. Thank you for your answer. – Drunken Daddy Jan 24 '17 at 14:00
1

Just resolved this issue, for a somewhat specific situation.

In my case, I was manually initializing the UICollectionViewController in a parent ViewController, and then adding the UICollectionViewController's view as a subview.

I resolved the issue by calling 'setNeedsLayout' and/or 'setNeedsDisplay' on the collectionView in the parent's viewDidAppear:

- (void)viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   [_collection.view setNeedsLayout];
   [_collection.view setNeedsDisplay];
}
Tim
  • 815
  • 9
  • 15
1

When using UICollectionViewDelegateFlowLayout be careful on what this function returns. Both width and height should be greater than 0. I used window as frame reference but due to complicated view hierarchy window was nil at runtime. If you need adjust the width of the element based on screen width use UIScreen.main.bounds.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: 
           UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {}
inokey
  • 3,243
  • 3
  • 18
  • 28
1

I was having a similar problem. But instead cellForItemAtIndexPath was called when the number of items was > 1, but not when there was only a single item. I tried many of the proposed solutions here and similar to many I found that it had to do with item sizing. For me the solution was to change the estimate size of the UICollectionView from Automatic to Custom.

Set estimate size to Custom

Wouter
  • 691
  • 7
  • 13
0

For me, I update the height of self.view (the collectionView's superview) via autolayout, and MUST call layoutIfNeeded after that.

[self.view mas_updateConstraints:^(MASConstraintMaker *make) {
    make.height.equalTo(@(height));
}];
[self.view layoutIfNeeded];
Mr. Ming
  • 2,317
  • 5
  • 28
  • 45
0

In my case I had to adjust the estimated Content size. content size did not do it.

  let layout = collectionVC.collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: 100, height: 100)
    layout.estimatedItemSize = CGSize(width: 100, height: 100)
tobi
  • 1
-1

I sunk a bit of time with this issue coming op with a CollectionView in a Contained view controller loaded from a storyboard. I ended up just deleting the contained view controller, and recreating it in the storyboard, and this seemed to get rid of the issue. My guess is there was some kind of storyboard corruption that had taken place.

wfbarksdale
  • 6,962
  • 11
  • 60
  • 87
-2

Setting collectionView frame inside custom cell's layoutSubViews() worked for me:

override func layoutSubviews() {
        super.layoutSubviews()
        let frame = self.contentView.bounds
        self.CollectionView.frame = frame
    }
Javal Nanda
  • 1,808
  • 2
  • 14
  • 25