Collection view carousels

Using the compositional layout — part 2

Sergio Ordine
5 min readAug 27, 2021

In the previous article the compositional layout elements were presented and how the will be applied to create our new breed group layout.

This article will focus on the code changes needed to implement this changes, based on the concepts defined earlier. The full code is available in the carousel branch of the reference project repository.

Remodeling the cell visualization

The first change is just a visual adjustment to make a cell more adequate to be presented on a horizontal scrolling carousel. The BreedRow does not hold any content or layout, as it was delegate to BreedView and BreedConfiguration.

We change the BreedView layout to a more suitable one, with the breed image with a layered semi-transparent label:

Fig. 1 — New BreedView cell

The same outlets were just update to use this new view configuration elements, so there is no significant change in the BreedView class itself. The HeaderView layout and class were kept unchanged. The configuration structures also were not updated. It indicates how this structure allows flexibility and specific changes.

Compositional Layout elements

We used a different view controller to implement this screen: CarouselViewController, that holds many similarities to the ListViewController. We will present the important code differences here, starting by the most dramatic change: the collection view layout. This is managed by the createLayout private function, and, more specifically, in the sectionProvider definition.

The sectionProvider is a UICollectionViewCompositionalLayoutSectionProvider closure. This function receives the section index and its environment to return a NSCollectionLayoutSection, describing the section layout for that specific section. In the previous examples, we have used a predefined list layout to create the list-like collection. The major changes from the previous code to this one lies exactly on the layout definition. Lets go through each one of the steps:

The first step of the sectionProvider is to define the item layout. The layout element must have a size specification related to its container (in the case of items, this will be its group) that is made using the NSCollectionLayoutSize class. This class asks for a width and height dimension, but using a flexible NSCollectionLayoutDimension class to define it.

The layout dimension provides four ways of defining a dimension size:

  1. Absolute — The absolute size specifies that dimension as a fixed number of points.
  2. Fractional Height — The fractionalHeight size specifies that dimension as a fraction of its container height.
  3. Fractional Width — The fractionalWidth size specifies that dimension as a fraction of its container width, in the same way as fractionalHeight.
  4. Estimated — The estimated size specifies an approximation for that dimension, that will be later calculated based on the auto layout to find its absolute size.

In this example, the item is defined occupies the whole frame of its group container, so it has a fractional size of 1 for both width and height.

We use that size to define the item using NSCollectionLayoutItem and passing to its initializer the relative size we defined.

In order to give some space for that item we can use insets on that item using the NSDirectionalEdgeInsets structure. After adjusting the item to occupy the whole group size, it leaves 5 points on each direction for that cell to breath…

The next step is creating the group layout:

The group is supposed to hold a square cell. In order to achieve this we define its size using a fraction of the section width for both the group height and width! This is a usefull resource to keep aspect ratios.

Then, we use NSCollectionLayoutGroup with a horizontal configuration, as the items will be presented side by side horizontally. The layout group also allow us to present them on a vertical configuration or even a custom composed configuration. The layout group also requires item layouts that will compose its pattern. In our case, the group just holds a single item layout.

We also use the insets to give same space between group and other groups or its containing section. Up to know the group holds 40% of the collection width (that is the full screen width), the same height to make it a square. The item holds its whole frame leaving 5 points in each direction as insets. Also each groups keeps a distance from the section borders. It is interest to notice that leading and trailing, what keeps the layout even for right-to-left language configurations, such as hebrew and arabic.

Then we must define the section layout:

The section uses the layout and group definition for its size, so there is no explicit size definition here. We just define that each section, which layout is defined by the NSCollectionLayoutSection class hold the defined group we are using.

The orthogonalScrollingBehavior defines how this section will scroll considering the main layout axis. This property make easy to make a section scroll horizontally on a vertical layout (such as our case) or vice-versa. We used the continuousGroupLeadingBoundary to make a continuous scroll that naturally stops for the leading position of the visible group, so we can completely see even the last breed card.

We also keep some insets from the main layout and sections. To be pretty honest here, the 1point leading is due to a strange behavior for the section header with groups that not completely fill the screen horizontally (the mixed breed group was the only noticeable issue).

The last step is adding the header as a supplementary element of this layout:

The header size is set to use the full section length and have an estimated height of 44 points (that will be adjusted by the autolayout calculation later).

The header is created as a supplementary view using the NSCollectionLayoutBoundarySupplementaryItem, that represents headers and footers as well as other suppelemtary views associated with the edges of a section. We also define its element kind as reference (header) to use it later and a relative location to align it in relation to its container (in this case, the top area of its section).

We also set its pinToVisibleBounds property to create the "sticky header" appearence. It will pin the supplementary view to the section while its items still being presented.

The last steps are defining this header in the set of the section supplementary views on its boundarySupplementaryItems property. this property is an array to set several supplementary views, but in this case we just have a single header.

The section provider finishes returning the section we composed from the items, groups and supplementary views above. We could also return different section layouts for different section indexes if it was needed.

Populating the cell image

The cell registration and data source creation is almost the same as the previous examples. We just made a change to asynchronously load the breed reference image:

The cell population provider now asynchronously load the breed image from its URL and creates a new configuration with this image and the breed name. The new configuration is updated on that cell, starting the content view update. Notice that, as this update is a UI operation, the contentConfiguration setting must be executed in the main queue.

With this changes, mainly in the configureLayout function, we can achieve the layout below:

Fig. 2 — New bred group layout
Unlisted

--

--

Sergio Ordine

Software developer and educator. If you want to support me and my content production, please buy me a beer at https://www.buymeacoffee.com/sergioordine