1

My app has a UIViewController class; inside this class I connect a UICollectionView loaded from a Storyboard.

I'm creating a custom layout with the UICollectionViewLayout class. Here's what it looks like:

class MyLayout: UICollectionViewLayout {

    override func prepareLayout() {
        super.prepareLayout()
    }

    override func collectionViewContentSize() -> CGSize {
        let attributes = super.collectionViewContentSize()
        return attributes
    }

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
        let attributes = super.layoutAttributesForElementsInRect(rect) as? [UICollectionViewLayoutAttributes]
        return attributes
    }

    override func layoutAttributesForItemAtIndexPath(indexPath:
    NSIndexPath) -> UICollectionViewLayoutAttributes {
        let attributes = super.layoutAttributesForItemAtIndexPath(indexPath)
        return attributes
    }
}

To assign a UICollectionViewLayout to the UICollectionView, I use the collectionViewLayout property of the UICollectionView:

myCollectionView.collectionViewLayout = MyLayout()

Running the app, the UICollectionViewCells are no longer visible. Though, they were visible before assigning the UICollectionViewLayout. I can now only see the background of the UICollectionView.

Why are cells no longer visibile?

Update

I looked carefully at the UICollectionViewLayoutAttributes of my UICollectionView, particularly the contentSize. I printed out its value and it seems to be equal to (0.0, 0.0). The attributes value for layoutAttributesForElementsInRect is also equal to nil. Definitely a red flag.

Download the project

Community
  • 1
  • 1
Cesare
  • 8,326
  • 14
  • 64
  • 116
  • Are you using AutoLayout? If yes, then you need to set the constraints – Sohil R. Memon May 06 '15 at 13:43
  • Yes, I'm using AutoLayout. Should I attach constraints to what? The `UICollectionView`? The `UICollectionViewCells`? Thanks! – Cesare May 06 '15 at 13:45
  • You need to give the constraints to UICollectionView, check out this: http://stackoverflow.com/questions/25804588/auto-layout-in-uicollectionviewcell-not-working – Sohil R. Memon May 06 '15 at 13:47
  • Check this example: http://randexdev.com/2014/07/uicollectionview/ – Sohil R. Memon May 06 '15 at 13:48
  • Thanks for the links. There seems to be no ways to create new constraints for the `UICollectionView`, nor the `UICollectionViewCell`. I implemented [this](http://stackoverflow.com/a/25820173/1135714) code into my `UICollectionViewCell` class but it made no effect. – Cesare May 06 '15 at 13:54
  • Well, why don't you go with interface builder? – Sohil R. Memon May 06 '15 at 13:59

2 Answers2

2

I think you can keep almost everything exactly the same. Change the class type of your custom layout to UICollectionViewFlowLayout.

class myLayout: UICollectionViewFlowLayout {
    //all your code here
}

Also, change myLayout to MyLayout for good measure :)

Frankie
  • 10,568
  • 2
  • 44
  • 55
  • Thanks for your answer @Frankie! I changed the type of the class but it the `UICollectionCells` still don't appear. I found out that the `contentSize` of the `UICollectionView` is equal to `(0.0, 0.0)`, which is definitely a red flag. It seems like the cells aren't displayed because the `UICollectionView` is too small. I'm looking to find a way to use a correct `contentSize`, even though I made sure AutoLayout constraints are created just fine. Thank you so much for your help! – Cesare May 06 '15 at 19:48
  • Yeah 0,0 is not right. I cleared all my constraints and it worked fine, so it still could be an issue with your constraints :/ What do you have in your `collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize` method? – Frankie May 06 '15 at 20:01
  • Thanks for helping me through this issue! In that method I only return this code: `return CGSize(width: 130, height: 130)`. I made sure the values of the width and the height math the ones in the Storyboard file. Maybe there are some other methods I am missing? Currently inside my `UICollectionViewController` I have the `cellForItemAtIndexPath`, the `sizeForItemAtIndexPath` and the `numberOfItemsInSection` methods. Do I need any other methods? Thanks! – Cesare May 06 '15 at 20:08
  • Does your collection view still work if you remove the custom layout? If so, then you're not missing anything. Let me know. – Frankie May 06 '15 at 20:32
  • Yes, it does. I guess there is something going on with the placement of the views of my app. In the `viewDidLoad` method I bring my cells to the top using this code: `self.view.bringSubviewToFront(cells)`. Nothing new happens, though. – Cesare May 07 '15 at 04:40
  • You're correct. Changing the class type to `UICollectionViewFlowLayout` works. Though, that class type isn't suitable for layouts that are not line or grid-based. Thanks again for your help! – Cesare May 07 '15 at 14:57
1

First of all, you should be initializing your UICollectionView with your layout instead of setting it afterward:

var collectionView = UICollectionView(frame: frame, collectionViewLayout: myLayout)

From the documentation:

You normally specify a layout object when creating a collection view [...]

Next, if you subclass UICollectionViewLayout, you must implement collectionViewContentSize and return a value of your own. Calling super is undefined here, unless you are subclassing UICollectionViewFlowLayout.

Subclasses must override this method and use it to return the width and height of the collection view’s content. These values represent the width and height of all the content, not just the content that is currently visible. The collection view uses this information to configure its own content size for scrolling purposes.

That's because UICollectionViewLayout as itself is an abstract class that does nothing (it is meant to be subclassed).

The UICollectionViewLayout class is an abstract base class that you subclass and use to generate layout information for a collection view.

In the same way, you also need to compute your own layoutAttributes in layoutAttributesForElementsInRect:. The easiest is to compute all your required layout in the prepareLayout method, and just get the ones you need afterwards. This is the "core" of a custom layout:

  • Ask the delegate for the number of elements you have to display in your collectionView.
  • For each element, first create the right indexPath, then create an empty layoutAttribute for that indexPath with UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath).
  • Compute and set the frame property of that layoutAttribute.
  • Store that layoutAttribute in a private property declared as a MutableArray.
  • In layoutAttributesForElementsInRect:, iterate through all your stored layoutAttributes previously instantiated, and return a subset of those with their frame intersecting the provided rect. The UICollectionView will ask its dataSource to provide a cell for every layoutAttributes returned by that method (via cellForItemAtIndexPath), and the frame of those cells will be set using the frame property in those layoutAttributes.

If you can read Objective-C as well as Swift, you could take a look at my sample UICollectionViewLayout implementation here, which makes the UICollectionView mimic the daily view of the iOS calendar app (screenshot).

If your goal is to achieve a layout that if fairly standard (i.e. elements disposed in a grid that flows horizontally or vertically), I'd recommend you to start by subclassing UICollectionViewFlowLayout instead, as it is already a valid implementation of UICollectionViewLayout, meaning you could use super in most methods to get the default values.

Also, a good read here in the official documentation on how to create your own custom layout.

Rufel
  • 2,570
  • 15
  • 15
  • Thank you very much for your answer! I'll check it out shortly. You made some good points there! – Cesare May 08 '15 at 18:18
  • @CeceXX you're welcome! Tell me if something is still unclear, working with custom layout can sometimes be troublesome. – Rufel May 08 '15 at 19:39
  • Thanks! I guess I must use `UICollectionViewLayout` to enable both horizontal and vertical scrolling. Dealing with the `layoutAttributesForElementsInRect:` is my only concern. What would you insert inside the `layoutAttributesForElementsInRect:` method to make my app work as expected? I still haven't understood what that method returns. Thanks again for your help! – Cesare May 08 '15 at 20:30
  • @CeceXX I added more details about `layoutAttributesForElementsInRect:` in my answer. I recommend you to compute all layouts in the `prepareLayout` method and store them in an Array, then iterate through them in `layoutAttributesForElementsInRect:` to return only those with a computed frame that intersect the one provided as a parameter. How you compute those frame is up to you, it will depend on how you want the cells to appear but they will usually be based on the item index and section. – Rufel May 08 '15 at 20:49
  • I couldn't ask for a better explanation! I'm now on the right track. Bounty coming in 1 hour :-) – Cesare May 09 '15 at 14:00