Move your cells left to right, up and down on iOS 13 — Part 1

Yuichi Fujiki
ShopBack Tech Blog
5 min readJul 12, 2019

--

Two blazingly simple ways to organize your complex collection of data

If you have used our GO section of our ShopBack app, you probably might have noticed that our home screen is vertically scrollable as a whole, yet some rows on the top are horizontally scrollable.

ShopBack GO home screen

To implement this type of interface that mixes vertical scrolling and horizontal scrolling, before iOS 13, it meant you need to implement nested UICollectionView, by setting inner UICollectionViewinstance(s) in outer UICollectionView's cell(s). (You could use UITableView for outer vertical scroll as well, of course) The structure is fine and well-experimented, but it has a potential to lead to messy code because you have to set up datasource/delegate for each collection view.

iOS 13 introduced two different ways to implement this kind of interface in a much easier way:

One is the much-hyped SwiftUI. In case you missed it, SwiftUI is a declarative DSL for writing user interface code in Swift and is considered to change the way we write iOS interface in several years.

Another one is less hyped but has as much potential even if the scope is not as big. iOS 13 introduced a new stock layout for UICollectionViewnamed UICollectionViewCompositionalLayout. This layout allows you to provide a completely different layout for each section with ease.

Sample Demo

In this tutorial-style article, I want to demonstrate how easy it is to implement the mix of horizontal/vertical scroll using the newly introduced techniques. We will create something like this.

It is a simple cat food advertising application. In the first section, you have a list of brand names, which you can horizontally scroll. The second section has images of cans, which scrolls horizontally as well. The last section has adorable images of our cats eating foods :), and this scrolls vertically along with the first two sections.

Now, let’s dive into the code! If you are not a coder/not interested in the code, TL;DR is that you or your engineer can implement such interface clean and easy to support only iOS 13 and up :)

UICollectionViewCompositionalLayout

First, we will demonstrate UICollectionViewCompositionalLayout. We will assume that you have basic knowledge of UICollectionView/UICollectionViewFlowLayoutand skip the initial setup of collection view along with datasource. In this example, we have

  • an array of brand names
  • an array of cat food images
  • an array of cats images

and datasource provides one UICollectionViewCell instance per item in the array. The collection view then usesUICollectionViewFlowLayout to layout all the cells along the vertical axis. The results would look something like this.

The entire view scrolls nicely in the vertical direction, but the first two sections have entire contents within the view width and cannot scroll horizontally. Yup, this is the good ol’ UICollectionViewFlowLayout :)

Constructor

In order to convert the layout to UICollectionViewCompositionalLayout, we will first createUICollectionViewCompositionalLayoutinstance:

Unlike traditional APIs for UITableView/UICollectionView that forced us to use delegate pattern, we can provide closure when instantiating UICollectionViewCompositionalLayout. The closure takes section index and environment (trait etc) and returns the layout of the specified section. In the switch block, we return layout info (i.e., size info) for each section in setup...Section()methods.

We will look at each method next.

Brand names section

Lets spit out the code first:

The layout of Brand Names Section

In the first part of setBrandNamesSectionmethod, we configure layout information of the section. The section always consists of group(s) and a group consists item(s). The layout hierarchy here can be visualized like this:

Here, the group and item are almost identical, since we have a rather flat hierarchy in this section. We will see the power of grouping in the next part of the tutorial.

One thing to note in the example is how we define the size. For item, we define the size using NSCollectionLayoutDimension.fractionalWidth/Height.

This means that the item occupies the entire width and height of the parent element, which is a group in this case.

And the group uses NSCollectionLayoutDimension.estimated/absolute to specify the size:

absolute(44) means that the group’s height is 44 points sharp. estimate(136)means that group’s initial width is 136 points, but could vary based on the content size. (NOTE: this part is not working as I expect. The pill size always stays at 136. Not sure if this is a beta bug or I am missing something.)

Down to here, we have specified all the sizes of item/group/section. We are only one magic line away from getting most wanted horizontal scroll:

💥💥💥 This line allows scrolling in orthogonal direction of the collection view’s scrolling direction (i.e., if the main scroll direction of the collection view is horizontal, the section itself scrolls vertically). .continuousGroupLeadingBoundary means that the items scroll continuously, but it snaps at the closest group’s leading edge when the user’s finger left the screen. For different scrolling behaviors, please look at the API doc.

Now we are essentially DONE for this section. But to add a nice touch, let’s add a section header. Section header and footer layout is defined by NSCollectionLayoutSupplementaryItem. To specify the size, you can use now familiar fractionalWidth/Height/estimated/absolute. By

we say that the header occupies the entire width of the section and is 44 points high. Alignment should always be .top for the header and .bottom for the footer unless you want an extraordinary layout.

You can use pinToVisibleBounds to make a sticky header.

Here is the final behavior of the section:

You can see that the scroll always stops at the leading boundary of the group as we specified continuousGroupLeadingBoundary for scrolling behavior.

Summary

In the first part of our tutorial-style article about iOS 13, we have looked at the newly introducedUICollectionViewCompositionalLayout and how you can use it to introduce a horizontally scrolling section in a vertically scrolling UICollectionView.

In the second part of the entry, we will complete the demo of UICollectionViewCompositionalLayout and will show how we can make the equivalent in SwiftUI.

Happy Coding!

--

--

Yuichi Fujiki
ShopBack Tech Blog

Technical director, Freelance developer, a Dad, a Quadriplegic, Life of Rehab