8

I created a collection view controller from story board, and set its custom class to ItemCollectionVC, the custom class of its cell to ItemCell, and set its reuse identifier to Cell

Here's my ItemCollectionVC class:

import UIKit

private let reuseIdentifier = "Cell"

class ItemCollectionVC: UICollectionViewController {

var dataSourceItems: [Items] = []
var counterBuildItems: [Items] {
    let weaponItemArray = WeaponItems.weaponItems as [Items]
    let defenseItemArray = DefenseItems.defenseItems as [Items]
    return weaponItemArray + defenseItemArray
}
var freeBuildItems = WeaponItems.weaponItems as [Items]
var captureKrakenItems: [Items] {
    let weaponItemArray = WeaponItems.weaponItems as [Items]
    let abilityItemArray = AbilityItems.abilityItems as [Items]
    return weaponItemArray + abilityItemArray
}

override func viewDidAppear(_ animated: Bool) {

    switch self.presentingViewController!.title! {
    case "CounterBuildVC":
        dataSourceItems = counterBuildItems
    case "FreeBuildVC":
        dataSourceItems = freeBuildItems
    case "CaptureKrakenVC":
        dataSourceItems = captureKrakenItems
    default:
        break
    }
}

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return dataSourceItems.count
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ItemCell
    cell.cellImage.image = dataSourceItems[indexPath.row].image
    print(dataSourceItems.count)

    return cell
}
}

When the collection view controller is presented, it's empty, what could cause the problem?

Bright
  • 5,699
  • 2
  • 50
  • 72

7 Answers7

15

One of three things caused this problem, pretty much every time I have encountered it, in a TableView or CollectionView:

1) Your ViewController is not the dataSource of your UICollectionView

2) numberOfRows or numberOfSections method returns 0

3) The height of your cell is 0, either due to constraint problems, or a heightForCell method being not/improperly implemented.

It's impossible to say which of these is your problem, and it's always possible that you've encountered something strange. Make certain that none of these is your problems, before exploring less likely options.

MAhipal Singh
  • 4,745
  • 1
  • 42
  • 57
dylanthelion
  • 1,760
  • 15
  • 23
  • 1. This is a UICollectionVC, not a UIViewController; 2. Both items and sections are not 0; 3. I don't have any constraints – Bright Oct 16 '16 at 08:10
  • A UICollectionViewController IS a UIViewController, and a CollectionView has a UICollectionViewDataSource as its dataSource (I'm pretty certain that your use of a UICollectionVC assigns your VC as the data source, though). I mentioned these, because I've encountered the exact problem you're having dozens times before, due to simple coding errors, and one of these has always been the cause. A cell with a height of 0 is always the toughest to debug, for what it's worth. If both numberOfSections and numberOfItems are being called, this is likely your problem. – dylanthelion Oct 16 '16 at 08:18
  • Also, correction (sorry): it's the containing view height/size, that can be the problem, not the cell size. Here are a couple of related issues: http://stackoverflow.com/questions/34847069/collection-view-isnt-being-displayed-cellforitematindexpath-is-never-called http://stackoverflow.com/questions/14668781/uicollectionviews-cellforitematindexpath-is-not-being-called – dylanthelion Oct 16 '16 at 08:24
  • I think it could have something to do with layout, Xcode gives me a misplaced view warning, and I could never fix it by clicking fix misplacement – Bright Oct 16 '16 at 09:06
  • Update: it's actually @Ahmad's answer worked, thanks for pointing things out anyways – Bright Oct 16 '16 at 09:12
  • No problem! Looks like numberOfCells was returning 0, because the UICollectionView was drawing before your data (array) was loaded into the data source? Sorry I wasn't more specific about the causes, there, kudos to @Ahmad, for getting the right answer, and glad you've got your project working! – dylanthelion Oct 16 '16 at 09:41
4

If you are pretty sure that the dataSource of the collectionView is connected to the viewController (it should be by default), then you should reloadData() because the collectionView reading from dataSourceItems. To understand the case, add a break point in cellForItemAt and add another one in viewDidAppear and check which one is called first?

override func viewDidAppear(_ animated: Bool) {

    switch self.presentingViewController!.title! {
    case "CounterBuildVC":
        dataSourceItems = counterBuildItems
    case "FreeBuildVC":
        dataSourceItems = freeBuildItems
    case "CaptureKrakenVC":
        dataSourceItems = captureKrakenItems
    default:
        break
    }

    collectionView.reloadData()
}

Hope that helped.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • 2
    Before adding a meaningless comment, as I mentioned in the answer, add two breakpoints and check what will happen. Cheers up :) – Ahmad F Oct 16 '16 at 08:48
  • But `viewDidAppear` gets called first, doesn't that mean there should be a problem without reloading data? – Bright Oct 16 '16 at 09:15
  • viewDidAppear called before cellForItemAt?! – Ahmad F Oct 16 '16 at 09:17
  • @BrightFuture This not should happened :) make sure that I'm talking about viewDidAppear, not viewDidLoad. cellForItemAt should run before viewDidAppear (viewDidLoad the first method should get called), so dataSourceItems is still empty, i.e numberOfItemsInSection is 0; After calling viewDidAppear, dataSourceItems should contains data... that's the case – Ahmad F Oct 16 '16 at 09:30
  • I know, but indeed break point stopped at `viewDidAppear` first – Bright Oct 16 '16 at 09:33
  • @Rroobb glad that you got it :) – Ahmad F Oct 16 '16 at 09:33
  • my bad! this is because no cells to draw at the first time!! instead, add it to numberOfItemsInSection and check is it zero? recheck it after reloadData get called – Ahmad F Oct 16 '16 at 09:35
  • 1
    It is 0 the first time `numberOfItemsInSection` is called, and 24 after `reloadData` – Bright Oct 16 '16 at 10:05
2

Some things that you could easily miss if you use storyboard

1) Don't forget that content in cell must have connected Top, Bottom constraint and content view must have height, by this cell will know to set height for cell. If you don't have these, cell height will be 0, and function cellForItemAt will never get called.

2) You can use cell layout to set dynamic cell and height for cell if you use this function:

    func collectionView(_ collectionView: UICollectionView,layout collectionViewLayout: 
    UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) ->
    CGSize {return CGSize(width: 20.00, height: 20.00)}
Egzon P.
  • 4,498
  • 3
  • 32
  • 31
2

I fixed my problem by initializing UICollectionView properly like the following:

fileprivate let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout.init()) 
Ishaan Gupta
  • 101
  • 1
  • 2
1

Check if you get the right number of sections in the numberOfItemsInSection method for the collection view.

If you are adding a flow layout to the collection view, remove the flow layout, and check if the collection view cells show now.

If they do, just adjust your collection view flow layout code it should look like this

let _flowLayout = UICollectionViewFlowLayout()
_flowLayout.sectionInset = UIEdgeInsets(top:0, left: 0, bottom: 0, right: 0)
_flowLayout.scrollDirection = .vertical
yourCollectionView.collectionViewLayout = _flowLayout

you can set the inset to fit your use.

0

You have to add your code in viewWillAppear, then it will work properly. /*

override func viewDidAppear(_ animated: Bool) {

switch self.presentingViewController!.title! {
case "CounterBuildVC":
    dataSourceItems = counterBuildItems
case "FreeBuildVC":
    dataSourceItems = freeBuildItems
case "CaptureKrakenVC":
    dataSourceItems = captureKrakenItems
default:
    break
}

}

Like this :-

override func viewWillAppear(_ animated: Bool) {

     switch self.presentingViewController!.title! {
case "CounterBuildVC":
    dataSourceItems = counterBuildItems
case "FreeBuildVC":
    dataSourceItems = freeBuildItems
case "CaptureKrakenVC":
    dataSourceItems = captureKrakenItems
default:
    break
}
}

*/

Mandeep Singh
  • 2,810
  • 1
  • 19
  • 31
  • When we create the class for UICollectionViewController, Xcode will automatically create this. You can create a class and check it. – Mandeep Singh Oct 16 '16 at 08:28
  • Actually, from the tutorial it clearly told me to remove this, and before I removed it it didn't work as well – Bright Oct 16 '16 at 08:29
  • @BrightFuture, Please change the background colour of collection view and cell. And see whether your collection view and cell are created or not. I am also facing the same issue right now. – Mandeep Singh Oct 16 '16 at 09:00
  • @MandeepSingh interesting method. The collection view is created, but the cell is probably not, as I can't see it. – Bright Oct 16 '16 at 09:03
  • I think @dylanthelion has a point, I am having some layout issues I guess, as Xcode gives me a misplaced view warning, and I could never fix it by clicking fix misplacement – Bright Oct 16 '16 at 09:05
  • @BrightFuture, My issue has been solved and my cells are shown properly with data. I can send you the demo if you want. – Mandeep Singh Oct 16 '16 at 09:22
  • Can you put your code on the answer so future viewers can see? – Bright Oct 16 '16 at 09:24
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/125828/discussion-between-bright-future-and-mandeep-singh). – Bright Oct 16 '16 at 09:35
  • @BrightFuture, Just use this code. It will surely helps you. – Mandeep Singh Oct 16 '16 at 09:47
0

In my case there was a problem with the collection view contentInset, try adding below code in your collection view sub class.


override func layoutSubviews() {
    super.layoutSubviews()
    self.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}

override func reloadData() {
    DispatchQueue.main.async {
        super.reloadData()
    }
}

Dhaval H. Nena
  • 3,992
  • 1
  • 37
  • 50