5

I have a UITableView with custom cells containing UIStackView. As part of preparing a new cell, when the Table View adds new cells via its cellForRowAtIndexPath method, it instantiates and adds any collection views (of type MultaCollectionView) and any UILabel which need to be added to the cell’s Stack View (a cell may include various collection views). In theory, a stack view contains a sequence of Labels and Collection Views.

Although labels are displaying correctly, the Collection View is not being displayed at runtime. The Collection View I’m attempting to display is defined in a .xib Interface Builder document.

The Collection View’s numberOfSectionsInCollectionView method is getting called, however the collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) method is never called.

Why is the collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) method never called? Why aren’t Collection Views being rendered in the Stack View?

import UIKit
private let reuseIdentifier = "Cell"

class MultaCollectionView: UICollectionView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.delegate = self
        self.dataSource = self
    }
    class func instanceFromNib() -> UICollectionView {
        return UINib(nibName: "multas", bundle: nil).instantiateWithOwner(nil, options: nil)[0] as! UICollectionView
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }


    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 2
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
        return cell
    }

}

The parent TableView adds Cells via the following method:

func addSubviewsToCellStack(cell: ArticuloTableViewCell, texto: [[String: String]]) {\            
    if isLabel == true {
            let label = UILabel()
            label.text = "blah"
            subview = label
            cell.textoStack.addArrangedSubview(subview)
        } else {
            let colview = MultaCollectionView.instanceFromNib() 
            cell.textoStack.addArrangedSubview(colview)
        }                        
    }

}
Himerzi
  • 809
  • 1
  • 8
  • 16
  • May be you are not setting cell's item size properly. Please set Item size of every cell within CollectionView's bound then check! – rushisangani Jan 20 '16 at 16:10
  • Try setting the background color of the collection view to, say, UIColor.greenColor, so you can verify it's not somehow zero pixels in size. – Graham Perks Jan 20 '16 at 20:07
  • 1
    stupid suggestion: do `self.collectionView.delegate = self`instead of self.delegate. – Oscar Apeland Jan 20 '16 at 20:10

4 Answers4

19

Since you are getting the call back for the numberOfSectionsInCollectionView this would suggest the datasource is connected up correctly but if a cell is not going to be visible based on the current layout then the collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) will not get called until it is needed. The reason a cell might not be visible could be because your sectionInsets for the collectionView are set large enough that the first cell would be positioned outside the visible area of the collectionView.

Another reason could be that the collectionView's frame is too small to display any cells. If your collectionview as a size of {0,0} you should get the calls as you are seeing to the numberOfSectionsInCollectionView but will not get the calls to cellForItemAtIndexPath since the cells will not be visible.

Perhaps try ensuring that your collectionView has a frame size that is large enough to display the cells you expect to see. You may be seeing the UILabels correctly because they have an implicit intrinsicContentSize which ensures they display well in a stackView whereas the CollectionView is just as happy to have a size of 0,0 as it would be with any other size. Try giving the collectionView explicit width and height constraints to see if that helps.

Dallas Johnson
  • 1,536
  • 10
  • 13
  • Thank you Dallas. Your answer has the right idea. Instead of adding constraints as you had suggested, I overrode the `UIView` `intrinsicContentSize()` method, and my cells began to display. I've added an answer specifying what I did. – Himerzi Jan 21 '16 at 17:29
  • 1
    That sounds like a good approach but be careful with relying on the intrinsic content size with compression resistance/hugging priorities. I think they will default to 750. So your collection view size could still get affected if another sibling view has stronger priorities that could take over. – Dallas Johnson Jan 21 '16 at 19:23
3

My hypothesis would be that since this is happening inside cells of a UITableView, whenever a table view cell gets put back in the "reuse pool", the delegates of the collection view get disconnected or disabled. This would depends on the code that provide cells to the table view (which we're not seeing in the code sample).

There could also be some code elsewhere that clears the datasource from the collection view. (unlikely but worth checking)

Alain T.
  • 24,524
  • 2
  • 27
  • 43
0

The issue was resolved by overriding UIView's intrinsicContentSize() method in my UICollectionView subclass as follows (check out this question):

override func intrinsicContentSize() -> CGSize {
    return self.collectionViewLayout.collectionViewContentSize()
}

As an aside, I had also forgotten to register a class for the Cell reuse identifier. So I modified the initializer:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    self.delegate = self
    self.dataSource = self
    self.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
Community
  • 1
  • 1
Himerzi
  • 809
  • 1
  • 8
  • 16
0

I FINALLY got it to work. When transitioning from a storyboard view controller to a non storyboard collection view controller you must do this while pushing to the controller :

func showChatLogController(user: User) {
    let chatLogController = ChatLogController(collectionViewLayout: UICollectionViewFlowLayout())
    chatLogController.user = user
    chatLogController.hidesBottomBarWhenPushed = true
    navigationController?.pushViewController(chatLogController, animated: true)

}

To be more specific you must use collectionViewLayout: UICollectionViewFlowLayout

Rohan Vasishth
  • 171
  • 1
  • 11