How to create a Section Background on a UICollectionView -SCSectionBackground

How to create a Section Background in a UICollectionView in Swift

I recently faced a small challenge on a UICollectionView, the following screenshot illustrates pretty well the situation. In short: I needed to have a transparent background in a UICollectionView to be able to see through some sections, but I didn’t want to be able to see the background in between the cells on some others.

Background colors per section on UICollectionView
Background colors per section on UICollectionView

Basically, I needed to be able to set different background colors per section. Well, needless to say that functionality doesn’t exist by default on iOS.

Luckily for me I found this great article: Changing the section background color in a UICollectionView from Eric Chapman who did an amazing job cutting down the layout fuss to reveal the bare minimum. As the project I’m working on is written in Swift, I transcribed and adapted it and wanted to share it with you.

Get ready to be amazed, you’re going to learn how to create a section background on UICollectionView and add color to it!

Note: for those not used to the CollectionViewFlowLayouts, don’t worry, I have a comprehensive article (2 articles actually) nearly ready to be published, sit tight!

The magic

The idea is to override UICollectionViewLayoutAttributes to add a color attribute. And then override UICollectionReusableView apply the color to the view background. Easy peasy :)

class SCSBCollectionViewLayoutAttributes : UICollectionViewLayoutAttributes {
 
    var color: UIColor = UIColor.whiteColor()
    
    override func copyWithZone(zone: NSZone) -> AnyObject {
        let newAttributes: SCSBCollectionViewLayoutAttributes = super.copyWithZone(zone) as! SCSBCollectionViewLayoutAttributes
        newAttributes.color = self.color.copyWithZone(zone) as! UIColor
        return newAttributes
    }
}

class SCSBCollectionReusableView : UICollectionReusableView {
    override func applyLayoutAttributes(layoutAttributes: UICollectionViewLayoutAttributes) {
        super.applyLayoutAttributes(layoutAttributes)
        let scLayoutAttributes = layoutAttributes as! SCSBCollectionViewLayoutAttributes
        self.backgroundColor = scLayoutAttributes.color
    }
}

How to use it?

The idea is to loop over the cells in the selected section and add a background to that cell (or row, or section… your choice) and apply the color to it. In my implementation, you can even change the color by indexPath. How flexible! All this happens in the UICollectionViewFlowLayout used on your UIcollectionView.

Here is an example of layoutAttributesForElementsInRect:

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

    let attributes = super.layoutAttributesForElementsInRect(rect)
    var allAttributes = [UICollectionViewLayoutAttributes]()
    if let attributes = attributes {
        for attr in attributes {

            // Look for the first item in a row
            // You can also calculate it by item (remove the second check in the if below and change the tmpWidth and frame origin

            if (attr.representedElementCategory == UICollectionElementCategory.Cell && attr.frame.origin.x == self.sectionInset.left) {
                // Create decoration attributes
                let decorationAttributes = SCSBCollectionViewLayoutAttributes(forDecorationViewOfKind: "sectionBackground", withIndexPath: attr.indexPath)

                // Set the color(s)
                if (attr.indexPath.section % 2 == 0) {
                    decorationAttributes.color = UIColor.greenColor().colorWithAlphaComponent(0.5)
                } else {
                    decorationAttributes.color = UIColor.blueColor().colorWithAlphaComponent(0.5)
                }
            
                // Make the decoration view span the entire row - or whatever you want ;)
                let tmpWidth = self.collectionView!.contentSize.width
                let tmpHeight = self.itemSize.height + self.minimumLineSpacing + self.sectionInset.top / 2 + self.sectionInset.bottom / 2 // or attributes.frame.size.height instead of itemSize.height if dynamic or recalculated
                decorationAttributes.frame = CGRectMake(0, attr.frame.origin.y - self.sectionInset.top, tmpWidth, tmpHeight)
                // Set the zIndex to be behind the item
                decorationAttributes.zIndex = attr.zIndex - 1
                // Add the attribute to the list
                allAttributes.append(decorationAttributes)
            }
        }

        // Combine the items and decorations arrays
        allAttributes.appendContentsOf(attributes)
    }
    return allAttributes
}

In my example on GitHub, I used a UICollectionView with 4 sections, 3 cells with green background on the even sections and 7 cells on a blue background on the odd ones. Once the magic applied, I got something like that:

Background colors per section on UICollectionView
Background colors per section on UICollectionView

You can find the full code on GitHub: SCSectionBackground.

I hope that helps, and don’t hesitate to tweak, update, customize it ;)