The previous part of this series covered the ground base for implementing a Compositional Layout.

Now that we are comfortable with the base implementation, this part of the series will focus on the Supplementary Items and Decorations that we can use in a Compositional Layout and the way to implement them.

Prerequisites:

This post contains specifics to describe Compositional Layout implementation. Be sure to download our starter project to follow along.

Suplementary Items

Supplementary Items implementation was redefined, now allowing us to anchor them to an item or a group, thus NSCollectionLayoutSupplementaryItem’s became more versatile and their implementation became simpler. NSCollectionLayoutSupplementaryItem’s position is now defined relative to its host geometry by making use of NSCollectionLayoutAnchor’s.

Creating a Badge

Since a NSCollectionLayoutSupplementaryItem became more versatile and we can achor it to an item, creation of badges for each item in the collection view becomes possible.

To showcase the implementation of badges, we will reuse the grid layout we have created in part 1 of this series, but first, we will need to create the badge item. To do so we are going to create a new function func makeBadge() -> NSCollectionLayoutSupplementaryItem that will create for us the supplementary item we will add to our item.

func makeBadge() -> NSCollectionLayoutSupplementaryItem {
        // 1
        let anchor = NSCollectionLayoutAnchor(edges: [.top,
                                                      .trailing],
                                              fractionalOffset: CGPoint(x: 0.5, y: -0.5))
        // 2                                      
        let size = NSCollectionLayoutSize(widthDimension: .absolute(15),
                                          heightDimension: .absolute(15))
        // 3                                  
        let badge = NSCollectionLayoutSupplementaryItem(layoutSize: size,
                                                        elementKind: "badge",
                                                        containerAnchor: anchor)
        return badge
    }

1 - Here we define an NSCollectionLayoutAnchor for our badge. By specifying [.top, .trailing] we position our badge in the top right corner of the parent, while the fractionalOffset positions our badge’s center in the the corner. A fractionalOffset of .zero would possition the whole badge inside the parent.

2 - As encountered before, we need to create a size for our item, so here we specify that we our badge should have a fixed value for width and height.

3 - We initialise our NSCollectionLayoutSupplementaryItem by assigning it the size, the anchor and an elementKind, based upon which we will identify our badge in collectionView:viewForSupplementaryElementOfKind:atIndexPath:.

Now that we have our badge ready, we just need to assign it to our items. To do so just replace NSCollectionLayoutItem initialiser in our func makeLayout with the following:

// 1
let item = NSCollectionLayoutItem(layoutSize: itemSize, supplementaryItems: [makeBadge()])

1 - Here we create the item for our layout and assign it the NSCollectionLayoutSupplementaryItem badge.

Run your code and you should see the following result:

Creating a Header

Previously, in our collection views, we could have encountered difficulties when trying to create and position header views. For example in a UICollectionViewFlowLayout the header would follow the same direction as the layout, making it impossible to use a supplementary view, if we would have liked our header to be above the content in a horizontal scrollable collection view. This is no longer the case because of the newly introduced NSCollectionLayoutAnchor, which allows us to possition our header anywhere in the parent.

For our header we will create an NSCollectionLayoutBoundarySupplementaryItem, that, similarly to a badge, we can anchor the way it better fits our needs.

So let’s go ahead and create a function that will make our header.

func makeHeader() -> NSCollectionLayoutBoundarySupplementaryItem {
        // 1
        let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
                                          heightDimension: .estimated(50))
        // 2                                  
        let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: size,
                                                                 elementKind: "header",
                                                                 alignment: .top)
        return header
	}

1 - Assign a size for our header. This will have the parent’s width and an estimated height of 50, that will allow for font scaling if needed.

2 - We initialise our NSCollectionLayoutBoundarySupplementaryItem by assigning it the size, the anchor and an elementKind, based upon which we will identify our badge in collectionView:viewForSupplementaryElementOfKind:atIndexPath:.

Now that we have our header, in our func makeLayout we need to pin it to our section. To do so add the following after the section initialisation code.

// 1
section.boundarySupplementaryItems = [makeHeader()]

1 - We assign the header as one of our section’s boundarySupplementaryItems.

Run your code and you should see the following result:

Decoration Items

A new design feature we started seeing a lot in iOS 13 is the new “Card Design”. When implementing this type of design it could come in handy if we can set a specific background to a section. This is now possible because of the introduction of NSCollectionLayoutDecorationItem.

Let’s start by creating a function that will make our background decoration item.

func makeBackgroundDecorationItem() -> NSCollectionLayoutDecorationItem {
        let backgroundItem = NSCollectionLayoutDecorationItem.background(elementKind: "background")
        return backgroundItem
    }

Now to be able to assign our NSCollectionLayoutDecorationItem to our layout we will need to add the following code in our func makeLayout().

... 
	// 1
    section.decorationItems = [makeBackground()]
...
	// 2
    layout.register(BackgroundDecorationView.self,
                    forDecorationViewOfKind: "background")
...

1 - Assign our decoration item to the section.

2 - Register the UICollectionReusableView we will use for our background item with the layout. This will be dequed automatically.

Run your code and you should see the following result:

Final notes

Compositional Layouts redefined the way we can build and design our layout by making it easier to implement suplemenraty items and introducing the concept of decoration items.

To learn more about Compositional Layout’s advanced features be sure to check out part 3 of these tutorial series.

Hope to see you next time!

References:

ios swift macos tvos ios13 iphone wwdc uicollectionview UICollectionViewLayout uikit

Author

Andrei Hogea

iOS Developer

iOS developer based in Copenhagen, Denmark. Always curious

You may also like

Building the App Store using Compositional Layout

In the first 2 parts of this series we learned the basics for implementing a Compositional Layout and how to add Supplementary Items and Decorations.

iOS
Compositional Layout Basics

Even though this year at WWDC Apple introduced the new SwiftUI framework, which will redefine the way we create our app UI’s from iOS 13, there were as well presented some advances in UIKit, precisely, in the way we can build an UICollectionView through the newly added Compositional Layout. With...

iOS