The past episodes have illustrated that creating a compositional layout isn't difficult or complex. The API of the UICollectionViewCompositionalLayout class is intuitive and flexible. In this episode, we revisit the feed view controller one more time. The plan is to display a title at the top of the feed view controller's collection view using a supplementary view.
Working With Supplementary Views
The ability to add supplementary views to a collection view isn't new, but compositional layouts make adding and managing supplementary views almost trivial. To display a title at the top of the feed view controller's collection view, we add a section header to the section of the collection view.
I already created a UICollectionReusableView subclass for the section title. You can find the class in the Views > Collection View Supplementary Views group. The subclass is named SectionTitleCollectionReusableView and its user interface is defined in SectionTitleCollectionReusableView.xib. The user interface is as simple as it gets. The SectionTitleCollectionReusableView class manages a single label to display the title it is given.

The implementation of the SectionTitleCollectionReusableView class isn't complex either. At the top, we define a static, computed property, supplementaryViewKind, of type String. This property defines the type or kind of the SectionTitleCollectionReusableView class. Don't worry about this computed property for now. The configure(with:) method is used to configure the view. The method accepts a single argument of type String?, which the view uses to configure its title label.
import UIKit
class SectionTitleCollectionReusableView: UICollectionReusableView {
// MARK: - Static Properties
static var supplementaryViewKind: String {
return "header"
}
// MARK: - Properties
@IBOutlet var titleLabel: UILabel! {
didSet {
// Configure Title Label
titleLabel.numberOfLines = 1
}
}
// MARK: - Public API
func configure(with title: String?) {
// Configure Title Label
if let title = title {
titleLabel.attributedText = title.toHeading(color: UIColor.Cocoacasts.blue)
} else {
titleLabel.attributedText = nil
}
}
}
Adding a Supplementary View
Let's put the SectionTitleCollectionReusableView class to use in the feed view controller. Open FeedViewController.swift and navigate to the didSet property observer of the collectionView property. We first need to register the XIB file of the SectionTitleCollectionReusableView class with the collection view.
We create a UINib instance, passing the name of the XIB file and a reference to the application bundle to the initializer. We pass the UINib instance to the register(_:forSupplementaryViewOfKind:withReuseIdentifier:) method of the UICollectionView instance. The first argument is the UINib instance. The second argument is the type or kind of the supplementary view. That is where the supplementaryViewKind static, computed property comes into play. The last argument of the register(_:forSupplementaryViewOfKind:withReuseIdentifier:) method is the reuse identifier of the supplementary view.
@IBOutlet var collectionView: UICollectionView! {
didSet {
// Configure Collection View
collectionView.delegate = self
collectionView.dataSource = self
// Create Collection View Layout
collectionView.collectionViewLayout = createCollectionViewLayout()
// Register Episode Collection View Cell
let episodeXib = UINib(nibName: EpisodeCollectionViewCell.nibName, bundle: .main)
collectionView.register(episodeXib, forCellWithReuseIdentifier: EpisodeCollectionViewCell.reuseIdentifier)
// Register Section Title Supplementary View
let sectionTitleXib = UINib(nibName: SectionTitleCollectionReusableView.nibName, bundle: .main)
collectionView.register(sectionTitleXib, forSupplementaryViewOfKind: SectionTitleCollectionReusableView.supplementaryViewKind, withReuseIdentifier: SectionTitleCollectionReusableView.reuseIdentifier)
}
}
This should look familiar if you worked with collection views and supplementary views in the past. The next step is updating the implementation of the UICollectionViewDataSource protocol. To display the section header, we need to implement the collectionView(_:viewForSupplementaryElementOfKind:at:) method of the UICollectionViewDataSource protocol. We ask the collection view to dequeue a SectionTitleCollectionReusableView instance, passing in the index path.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
// Dequeue Section Title Collection Reusable View
let supplementaryView: SectionTitleCollectionReusableView = collectionView.dequeueReusableSupplementaryView(ofKind: SectionTitleCollectionReusableView.supplementaryViewKind, for: indexPath)
}
The method we invoke to dequeue a SectionTitleCollectionReusableView instance is a helper method and very similar to the method we use to dequeue collection view cells. Under the hood, it invokes the dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:) method of the UICollectionView class. You can find its implementation in UICollectionView+Helpers.swift.
func dequeueReusableSupplementaryView<T: UICollectionReusableView>(ofKind kind: String, for indexPath: IndexPath) -> T {
guard let supplementaryView = dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
fatalError("Unable to Dequeue Reusable Supplementary View")
}
return supplementaryView
}
The section header should display the text Feed, the title of the feed view controller. We pass the title property of the FeedViewController instance to the configure(with:) method of the SectionTitleCollectionReusableView instance. We return the SectionTitleCollectionReusableView instance from the collectionView(_:viewForSupplementaryElementOfKind:at:) method.
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
// Dequeue Section Title Collection Reusable View
let supplementaryView: SectionTitleCollectionReusableView = collectionView.dequeueReusableSupplementaryView(ofKind: SectionTitleCollectionReusableView.supplementaryViewKind, for: indexPath)
// Configure Supplementary View
supplementaryView.configure(with: title)
return supplementaryView
}
Sizing and Positioning Supplementary Views
The collection view doesn't know how to size and position the section header and it doesn't need to worry about this. That is the responsibility of the compositional layout. We need to revisit the createCollectionViewLayout() method. The idea is similar to sizing and positioning the items of the collection view. The API should look familiar.
We create an NSCollectionLayoutSize instance to define the size of the section header. The section header should span the width of the collection view. To accomplish that, we invoke the fractionalWidth(_:) type method of the NSCollectionLayoutDimension class, passing in 1.0 as the argument. We don't know the exact height of the section header at compile time. This isn't a problem, though. We invoke the estimated(_:) type method of the NSCollectionLayoutDimension class, passing in 44.0 as the argument.
private func createCollectionViewLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
...
// Configure Section
section.contentInsets = NSDirectionalEdgeInsets(top: 0.0, leading: 20.0, bottom: 0.0, trailing: 20.0)
// Define Section Header Size
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
return section
}
}
The next step is creating an instance of the NSCollectionLayoutBoundarySupplementaryItem class. The class is in many ways similar to the NSCollectionLayoutItem class. As the name suggests, the NSCollectionLayoutBoundarySupplementaryItem class defines the size and position of supplementary views.
The initializer accepts three arguments, the size of the supplementary view, the type or kind of the supplementary view, and the alignment of the supplementary view. The third parameter, an NSRectAlignment object, defines the alignment or position of the supplementary view in the section. Because we want to add a section header, we pass top as the third argument of the initializer.
private func createCollectionViewLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
...
// Configure Section
section.contentInsets = NSDirectionalEdgeInsets(top: 0.0, leading: 20.0, bottom: 0.0, trailing: 20.0)
// Define Section Header Size
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
// Create Section Header
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: SectionTitleCollectionReusableView.supplementaryViewKind, alignment: .top)
return section
}
}
Supplementary views are associated with a section. To associate the section header we created with the NSCollectionLayoutSection instance, we set its boundarySupplementaryItems property. The boundarySupplementaryItems property is of type [NSCollectionLayoutBoundarySupplementaryItem].
private func createCollectionViewLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
...
// Configure Section
section.contentInsets = NSDirectionalEdgeInsets(top: 0.0, leading: 20.0, bottom: 0.0, trailing: 20.0)
// Define Section Header Size
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
// Create Section Header
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: SectionTitleCollectionReusableView.supplementaryViewKind, alignment: .top)
// Set Boundary Supplementary Items
section.boundarySupplementaryItems = [ sectionHeader ]
return section
}
}
Let's build and run the application to see the result. This looks good and it is the result we were after.

Before we move on, I want to show you how easy it is to make the section header sticky. Revisit the createCollectionViewLayout() method in FeedViewController.swift. To make the section header sticky, we set the pinToVisibleBounds property of the NSCollectionLayoutBoundarySupplementaryItem instance to true. That's it. Build and run the application to see the result.
private func createCollectionViewLayout() -> UICollectionViewLayout {
return UICollectionViewCompositionalLayout { (section, environment) -> NSCollectionLayoutSection? in
...
// Configure Section
section.contentInsets = NSDirectionalEdgeInsets(top: 0.0, leading: 20.0, bottom: 0.0, trailing: 20.0)
// Define Section Header Size
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44.0))
// Create Section Header
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: SectionTitleCollectionReusableView.supplementaryViewKind, alignment: .top)
// Configure Section Header
sectionHeader.pinToVisibleBounds = true
// Set Boundary Supplementary Items
section.boundarySupplementaryItems = [ sectionHeader ]
return section
}
}

What's Next?
The past episodes have taught you the basics of compositional layouts. It is important that you know what items, groups, and sections are and how they relate to one another. In the next episodes, we implement the user interface of the library view controller. The layout of the collection view of the library view controller is more advanced so it is important that you have a good understanding of the basics of compositional layouts.