Create your own UICollectionView API
In this article, we will create an external framework that will expose some cells, a background and horizontal line decorationViews and one vertical custom UICollectionViewFlowLayout
that manages margins and paddings between cells.
Why using UICollectionview ?
According to your app, using UICollectionView
to build your screens could save you a lot of time, mostly if your screens are data-oriented and contextual.
It’s easier to add a cell between two others than updating constraints to show or hide an UI element.
A powerful feature of UICollectionView
is DecorationView
. Without adding complexity on your cells, you can add backgrounds behind some of them and reuse them in the whole app.
Finally, UICollectionView
allows to animate changes in your screens with just a few lines of code.
Why an API ?
Most of the time, app screens are designed with the same design: same buttons, same elements in many screens with just different contents but same layout.
The purpose to create an API is to use these elements in the simplest way. The main goal of an UICollectionView
API is to spend less time on building screens and to spend more time to implement and test the logic, improve details and have a more maintainable code.
Another advantage is to provide an overall UI consistency in the app and to create screens as closely as possible to the mockups given by your(s) designer(s).
Collor
The API provided by Apple for implementing UICollectionView
is indexPath-oriented. At oui.sncf, we created an over-layer, called Collor, to have a data-oriented implementation. The main goal of this library is to describe, in one file, the structure of your UICollectionView
.
Before continuing reading this article, you should take a look at these two articles:
Let’s go
1) Creating your own framework
For optimising compilation time and minimising dependencies, the best way is to create a framework in your workspace. We name it CollorAPI. If you project to use this framework in others projects, I suggest you to create a private pod.
We don’t explain the process of creating a framework here, you can find a lot of tutorials if you need one:
2) An unique section descriptor
Most of the time, an unique customisable section descriptor is sufficient for our need.
The second class we create in our new framework is a subclass of CollectionData
inside of which we add a method to instantiate the section descriptor created just before:
Right now, we can add sections easily:
3) The builder
Next step it’s the creation of the builder
! It’s the object that will expose methods in order to facilitate the life of your team. For now, the builder
exposes two functions to add a custom descriptors and a LabelCell
:
Instead of passing as parameter an array of cells in the closure used to build a section, we pass the builder
, then we save the informations owned by the builder
in the section descriptor. Pretty simple ;)
Ok, it’s cool, our API becomes useful: for each type of generic cell (which is used at least twice in the project), the builder
exposes a method to create one easily and it indexes all of these. At oui.sncf, we created more than 50 reusable cells and it’s not over yet.
4) Vertical spaces
Each generic cell must be designed edge-to-edge. Then margin and padding will be handle in the layout of the collectionView and with section insets. By applying this rule, it is easier to respect design screens without adding complexity in the cell or adding empty cells to fill in the gaps.
First, we implement this feature in the builder
:
Each time we need to add spaces between two cells, we just call builder.add(verticalSpace: .huge)
. This sample is simplified: It’s impossible to add spaces more than once and to add space at the beginning of a section (before the first cell). You can do that later, according to your need.
Spaces are stored in a dictionary, this dictionary is passed to the section then used in our custom layout:
Each time there is a verticalSpace, the globalOffset
is incremented then the next cell origin is offset by this value. The content size is also offset by this value. With this code, you can now add spaces between cells with an elegant way and without dirty tricks :) Don’t forget to set the the custom layout as layout of the collectionView.
5) Decorations
The goal of decorationViews is to add some graphic parts behind cells but without altering them. In CollorAPI a decoration is represented by a decorationBlock. A decoration view needs an origin and a size: the origin is defined with the origin of the the first cell and the size is computed by the right bottom corner of the last cell (in a vertical layout). To get these informations, the decoration stores some properties:
- the
sectionDescriptor
(for retrieving section index and some other informations) startItemIndex
andendItemIndex
(for computing indexPaths and retrieves cell attributes in the layout)type
: the decorationView type
Now, we just have to add this feature on our builder
with two new methods:
startDecorationBlock(:)
: get the current index of the section and begins a decorationBlock.endDecorationBlock(:)
: get the current index of the section and ends the decorationBlock.
VerticalCollectionViewLayout
is updated to handle decorationBlocks:
We loop on all decorationBlocks then, according to the type, decorationViews are created using layoutAttributes
of cells computed just before with verticalSpaces.
Each time you need a new kind of decorationView, you just have to add a new entry in the enum DecorationBlockType
and update the layout to handle it.
Finally, the collectionData looks like this:
You can download sources of the sample on github (SampleCollorAPI branch). In this example, there is another type of DecorationBlockType
: .horizontalLine
. As its name suggests, it enables to add horizontal line between cells. You are free to improve the code for even more flexibility.
6) To go further
At oui.sncf, there are 11 decoration types. They enable to add a ticket background or build a journey timeline. For the timeline we created a nested builder inside the section builder. By this way, roles are well separated and the API remains simple to use by other developers. Cells are are shifted to the right in the layout in order to reuse them instead of creating new ones with inside offset.
To resume, in this article we went through the bases of your future framework. It is now up to you to make it evolve according to your needs!
Thanks for reading, and let me know what you think on Twitter.