Why are UICollectionViewCells no longer visibile a

2019-06-07 04:40发布

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

2条回答
beautiful°
2楼-- · 2019-06-07 05:10

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

查看更多
ら.Afraid
3楼-- · 2019-06-07 05:15

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.

查看更多
登录 后发表回答