In this chapter, we are going to discover another convenient, flexible, and efficient tool to present dynamic content: collection view.
Collection view
Collection view is represented by UICollectionView
class and incorporates a set of sub-elements that allows to display items in the form of a grid.
The direct sub-elements of a collection view are:
Header
Footer
Items. In the context of the collection view, the term item refers to a cell.
Cells can be grouped into sections... sound familiar?
Those definitions may remind you of table view ( UITableView
) - and rightly so, as they share many concepts.
Let's add a collection view object to the list view controller on the storyboard:

Make sure that it's positioned and sized properly - we want it to take up all of the available space on the screen:

In order to make our view controller code work with the view object, we need it to implement delegate protocols - UICollectionViewDelegate
and UICollectionViewDataSource
.
We could do that in the subclass of our UIViewController
class:
class MediaListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
...
}
This time around we'll use a different, more elegant approach - to achieve the same result - using extensions.
Extensions
Extensions are literally extensions on a class. They allow you to extend a class without subclassing or modifying the original class.
Why is it "cooler" than subclassing or modifying the original class?
Subclassing forces us to use the subclass, which means that we have to modify our code if we want to use the implemented functionality.
And modifying the original class may become too clumsy. Besides, we can't modify the Swift API classes, but we certainly can extend them! 😏
When working with collection views within our view controllers, we are adding functionality to our custom subclass - MediaListViewController - which conforms to required collection view protocols. This will allow us to keep the code clean and to easily separate.
Could we just use #MARK to separate the code?
Using #MARK is one solution; however, in terms of code management, extensions provide the ability to distribute code between different files!
To implement an extension, use the keyword extension
instead of class
:
extension MediaListViewController: UICollectionViewDataSource {
}
extension MediaListViewController: UICollectionViewDelegate {
}
Next, we need to connect our collection view to the code to create an outlet...
@IBOutlet weak var collectionView: UICollectionView!
...and implement a helper config
method that will assign the data source and delegate:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
config()
}
func config() {
collectionView.delegate = self
collectionView.dataSource = self
}
And now, of course, we get some complaints that we don't conform to the declared protocols; the required methods are for the data source and are very similar to those needed for the table view. To serve the data, let's declare a property that will temporarily serve an empty array of objects we're going to use to populate the grid:
var dataSource: [MediaBrief] {
return [MediaBrief]()
}
And then complete the conformance to the dataSource protocol:
extension MediaListViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return dataSource.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return UICollectionViewCell()
}
}
Where else can we use extensions?
We can use extensions for many purposes, including adding or altering the functionality of existing Swift classes. For example, we can programmatically implement custom colors beyond those that are provided by the UIKit framework and use them as if they were always there. 🎨
Here's a simple example:
extension UIColor {
static let sunshine = UIColor.yellow
static let forest = UIColor.green
struct UIPalette {
static let backgroundColor = UIColor.sunshine
static let titleColor = UIColor.forest
static let subtitleColor = UIColor.blue
}
}
And then, we can use it in our code as if the features of the extension were part of the original class:
view.backgroundColor = .sunshine
view.backgroundColor = UIColor.UIPalette.backgroundColor
Notice how we also grouped the palette colors into a structure? It makes the code more readable and easier to maintain.
Could we just assign colors directly?
We could assign the palette colors to the UI elements directly; however, the code is more flexible when specific colors are abstracted. For example, should we decide to use green as an accent color, we'd only need to reassign a new color to the UI color abstraction which would then appear in all parts of the code! ✨
Extensions are a very powerful and useful component of Swift language!
Understanding the collection view flow layout
Collection view offers flexibility in the way we lay out elements (its items, or cells). Dealing with the layout requires an associated object called "Collection View Flow Layout" represented by a class UICollectionViewFlowLayout
.
Let's take a closer look at the storyboard navigation. When we dragged a collection view object to our list view, it was added with two sub-elements:
Collection View Cell - an expected cell prototype that we'll utilize shortly
Collection View Flow Layout

The collection view flow layout is responsible for the direction of items scrolling on the collection view, item sizing, etc. It's represented by a class UICollectionViewFlowLayout
, and we need to create an outlet for it in order to be able to customize it programmatically.
@IBOutlet weak var collectionViewFlowLayout: UICollectionViewFlowLayout!
For now, we are going to only set the scroll direction in the config method:
collectionViewFlowLayout.scrollDirection = .vertical
We can also specify various general attributes. Some handy ones are:
estimatedItemSize
oritemSize
- to provide an estimated size for the items or an exact size, respectively.minimumInteritemSpacing
orminimumLineSpacing
- to provide spacing between items and lines, respectively.
We can also provide individual values based on section and even item within each section. For that, our view controller must conform to UICollectionViewDelegateFlowLayout
protocol, which we can also declare as an extension:
extension MediaListViewController: UICollectionViewDelegateFlowLayout {
}
Now we can place our sizing and spacing delegate methods within it:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let w = collectionView.frame.size.width
return CGSize(width: (w - 20)/2, height: 290)
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
return 20
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 30
}
Let's do one more thing to this view - set a navigation title programmatically in the config method:
navigationItem.title = "ListenUp!"
It's time for a quick test - click Run!

Not much going on at the moment...
No wonder! We haven't provided any items as data source. If we did, the app would crash as we haven't provided any suitable cells in the collection view cellForItem method. You can try it out yourself - change the dataSource property to return sample data.
Storyboard vs programmatic implementation
You can also configure many of the properties of both collection view and its flow layout using Attribute inspector in storyboard.
On the other hand, you can also create all the UI elements programmatically instead of adding and configuring them in the storyboard.
Which approach to choose?
It depends. 😅 Using storyboards is always a beneficial way to start and play around. As projects and teams grow, it might be challenging to maintain more sophisticated storyboards. They are often abandoned altogether in favor of programmatic implementation of interface.
It's also possible to use a mix-and-match approach and implement visually select components and reuse them programmatically in the code.
Moving on to further customization - we need the cells for data! 😁
Let's recap!
Collection views provide a grid layout for content elements and are represented by the class
UICollectionView
.Collection view delegate (typically a view controller) must conform to two protocols:
UICollectionViewDelegate
andUICollectionViewDataSource
.To complete the minimal conformance to the delegate protocols, the delegate must implement all required methods of
UICollectionViewDataSource
protocol.Extensions can be used to extend or alter the functionality of classes, structures, protocols, or enumerations without subclassing or modifying the original implementations.
Extension functionality becomes available (as if it were part of the original object) anywhere in the code that has access to the extension.
Collection items layout is managed by a component called collection view flow layout, which is represented by the class
UICollectionViewFlowLayout
and controls various characteristics, i.e. the direction of scrolling or item sizing.