Create the App Store with UICollectionView Compositional Layouts
Long live the King:
UIKit is not yet dead, and creating great
UICollectionView layouts is certinly part of that.
Here I’m going to re-create the App Store using Compositional Layouts. Oh, and
DiffableDataSource. This is going to be ace!
Difficulty: Beginner | Easy | Normal | Challenging
This article has been developed using Xcode 11.4.1, and Swift 5.2.2
UICollectionViewCell: The on-screen cell for the UICollectionView type
UICollectionView: An object that manages an ordered collection of data items and presents them using customizable layouts
UICollectionFlowLayout is great and is a concrete Layout given to you for use creating your Apps. But what if you want to create a more complex layout (the App Store layout is an often-chosen example of this) including multiple scrolling sections you’re likely to run into a problem (or two). In steps *Compositional Layouts* to help you with just that.
*Compositional Layouts* are a peer of
UICollectionFlowLayout so we can take the theory we already know from that layout here — Composition instead of subclassing.
Groups that allow different layouts within a section in a
The classes for compositional layouts
NSCollectionLayoutSize The size
NSCollectionLayoutItem The cell that is rendered on screen
NSCollectionLayoutGroup The group that contains the
NSCollectionLayoutSection Sections compose composational layouts
Diffable data source
The data source for this solution is a
DiffableDataSource (sorry for those not familiar with this). However since
UITableViewDataSource replaced by
DiffableDataSource this is a more declarative way of using the
Diffable data source uses
NSDiffableDataSourceSnapshot and sections are appended to it before the result is rendered to the screen. The
UICollectionViewDiffableDataSource itseld allows you to render the cells for the
UICollectionView. There is a full guide for DiffableDataSource that you can peruse.
You’ll see how these are used, right below!
The UIViewController where I have placed the code for the Apps page is in
AppsCompositionalLayout.swift in the repo.
Placing a button on the navigation bar
An initial issue with creating this — the large navbar title disappears when the user scrolls, and the profile image slides up.
Know what I mean? Here is the animated GIF:
This is using SF Symbols (so
UIImage(systemName: “person.crop.circle”) works) to provide the icon on the top right hand corner.
My approach has been to create a function to
setupNavBar() as an extension on the relevant view controller. The icon is then pinned to the
UILabel so it can slide nicely.
In the repo I’ve named this rather attractively as
AppsCompositionalLayout-setupNavBar.swift, for your convenience.
I’ve created a
LayoutSection that each section of my little App store will conform to. By section I mean in the following image there are two sections — the red section is
FeatureSection and the purple section is
So each of these conforms to the
protocol as shown here:
Which announces that anything conforming to the protocol means it must return an
NSCollectionLayoutSection which will inform the
UICollectionViewCompositionalLayout which layout is going to be used (it will be called from the view controller, more on that later). If a particular
LayoutSection does not have a header the
protocol extension will return nil — and we don’t have to declare the
header function to conform to the
Now I have 7 sections that conform to this
protocol, and of course they are all available in the repo that I really recommend you download.
enum has been defined to indicate these 7 sections
which are then placed in a
lazy var which also derives the order of the sections
Let us see how an individual
LayoutSection can be created, as shown in the code snippet below:
This is a practical example (yes!) of formulating the section.
But what of the other sections? Some of these need headers and footers. These conform to
UICollectionReusableView but look much like any
UIView I’ve made this reusable so it is quite long for something that appears to create just a title as a heading. Take a look at the repo for details — The file name is
TitleSupplementaryView.swift (but you must remember to register it like a
Notice here we declare the estimated height of the header — included in the layout size.
UICollectionViewDiffableDataSource leverages the sections
enum defined, and the
protocol that allows us to configure each cell for the relevant index path. The use of
AnyHashable is covered in the why why why section below!
Configuring the header
The header is a property of the
dataSource, as in this example we can return the appropriate header for each
Section that is available from the
enum here featured in the relevant
UIViewController. I did put this into a function that is called in
Once the header has been set up (this, one again, has been performed in
viewDidLoad()). As you can see a snapshot has been set up and each
Section is appended to to it — before being applied to the
I’d put the
UICollectionViewCell instances in the pot as “nothing interesting”. I have created some programatically with constraints, and raised others from the
This isn’t the focus of this article, but to save everyone having to download from the repo here is how I created the standard App Cell:
with the code (including a closure for clicking on the button of the cell).
Oh, the cell modifies it’s own size so needs
func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes in the
WHY WHY WHY
This is an article rather than production code. I developed this for an iPhone SE, and some of the padding for the
Layout is absolute no matter the size of the device — this is something that should be thought about if you are thinking of doing something like this for production code.
AnyHashable has been chosen for the type in the snapshot and datasource:
var dataSource: UICollectionViewDiffableDataSource<Section, AnyHashable>! . Now usually instead of choosing
AnyHashable it would be pertinent to choose the model type of the data being displayed. However, in this case I have used
AnyHashable to be able to display any type of data in the cells (so display both single links and the more complex App types). Would you use
AnyHashable in production code? Probably not, and might come up with a
protocol that all the display types could conform to. In this case, however, it is probably easier to read and understand
AnyHashable for beginners. You can should me on Twitter any time you’d like.
Oh, and I didn’t implement all of the buttons one each cell (or on the
UINavigationBar), but indicated how that might be done with the closures on one or two of the cells.
Yes, the way I’ve created the data to be displayed makes me feel sick. It will make you feel sick to. In real life this will come from an API and be decoded in Swift.
These in totality would be a great extension to this, as would producing all of the other tabs of the project — perhaps someone wants to help me out with this (or not!)
This article uses
UICollectionViewDiffableDataSource to create a clone of the Apple App Store. This has given a good opportunity to use features of iOS13 in quite a creative way.
What really surprised me is my main
AppsCompositionalLayout is so small — there is a great separation of concerns here and this is a really easy project to debug and add to.
Isn’t this what we are all looking for when we produce software? This is why these APIs are so nice and we should think about incorporating them into a project. At least consider it, and having this type of example will only help us all (I hope).
I hope this article has been of use to you, and that you have enjoyed reading it.
The code from this is included in the attached Repo.
If you’ve any questions, comments or suggestions please hit me up on Twitter