Do we even need Compositional Layouts to design our next iOS app?
Tags: swift, ios
Published: 2021-06-17 21:58:32

Apple has a great eye for design as can be seen from its clean, elegant and clutter free interfaces. They provide the developers, equal opportunity to create the best experience for the end users. Whether one wants a list layout like in the iPhone Contacts app or a grid layout like in the Instagram app, layout APIs such as `UITableView` and `UICollectionView` can be used as per the requirements. These APIs have existed for a long time; `UITableView` exists since iOS 2 (year 2008) and `UICollectionView` exists since iOS 6 (year 2012).


### Introduction

For today, our focus will be to know about **Collection View Layout** and **Advances in Collection View Layout**. 

`UICollectionViewLayout`, as per Apple’s definition, *is a line-based system that allows you to lay out on the orthogonal axis, of the layout axis, until you just fill up the amount of space you have available and we drop to the next line.*

`UICollectionViewLayout` helps us to achieve simple line-based layouts with little configuration and little customisation. In order to create advanced layouts, we need to subclass `UICollectionViewFlowLayout` or create our custom layout by subclassing `UICollectionViewLayout` directly. We have extensively used and continue to use this layout, but if we wish to have multiple scrolling sections and variably sized tiled layouts like in AppStore, it’s very difficult to do so with `UICollectionViewFlowLayout`.

In order to make things easier, Apple decided to make some advancements and upgraded the capabilities of `UICollectionViewLayout`. They introduced a layout API, which is capable of handling current day complex designs, and is known as **Compositional Layouts**. Compositional Layouts were introduced with iOS 13 in the year 2019.

### Implementation

We are going to discuss and compare two approaches, the **Traditional layout approach** and the **Compositional layout approach**.

Let’s define our requirements first. 

I would like to design an interface, 

1. Which scrolls vertically like a list,
2. Has a header with each row, and
3. Each row scrolls horizontally


#### Traditional Layout
As per our requirements, we can see we will have a design as below *(1A)*. 
* We have the entire screen covered with `UITableView` to support vertical scrolling. 
* We place a combination of `HeaderView` and `UITableViewCell` in each `UITableView` section. 
* Each `UITableViewCell` will have a `UICollectionView` to support horizontal scrolling. 
* And each `UICollectionView` is comprised of multiple colourful `UICollectionViewCell`.

![Traditional Layout Approach](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czz1shtqg9jdsn3ke5xv.png)
 
 


The `UITableView` data source methods will look like the below code snippets *(1B, 1C, 1D)*. We create `class TraditionalVC` and another `class TableViewCell` to have `UICollectionView` data source within `UITableViewCell`. In order to further modularise and customise the code, we will have to create a `class HeaderCell` and a `class CollectionViewCell`. But for now, we are keeping it simple and just use two classes for a basic implementation.
```
// 1B - UITableView data source in class TraditionalVC
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
    return cell
}
```
```
// 1C - UITableView delegate in class TraditionalVC
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    guard let header = tableView.dequeueReusableCell(withIdentifier: "HeaderCell") else { return UITableViewCell() }
    return header
}
```
```
// 1D - UICollectionView data source in class TableViewCell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
    return cell
}
```

 

#### Compositional Layout
Now, let’s talk about Compositional layouts approach. 

 
![Compositional Layout Approach](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1kfbr3dbalt766n5k40o.png)
 

From the above design *(2A)* and the below code snippet *(2B)*, we can see there are **4 core components** in this layout:

1. **Item**: You can think about it as the `UICollectionViewCell`, which renders on the screen.

2. **Group**: They’re used to describe the layout of our items within a section. We can provide groups with horizontal, vertical or even custom layouts within the sections.

3. **Section**: It’s the same as sections in `UICollectionView` and `UITableView`. We can define the number of sections in `UICollectionView` data source and the items in each section.

4. **Layout**: It’s the entire space, the elements occupy on the screen.

```
// 2B - Create Compositional Layout
func createLayout() {
    let size = NSCollectionLayoutSize(widthDimension: .absolute(250.0), heightDimension: .absolute(150.0))

    // Item
    let item = NSCollectionLayoutItem(layoutSize: size)

    // Group
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])

    // Section
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous

    // Header in section
    let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.99), heightDimension: .absolute(40.0))
    section.boundarySupplementaryItems = [.init(layoutSize: headerSize, elementKind: "HeaderKind", alignment: .topLeading)]

    // Layout
    collectionView.collectionViewLayout = UICollectionViewCompositionalLayout(section: section)
}
```



We can also define layout size for each item and group. The size can be defined using:

* **.fractionalWidth**: The value of `.fractionalWidth` ranges between 0 and 1. If `.fractionalWidth` is 0.5, then it will occupy 50% width.

* **.fractionalHeight**: The value of `.fractionalHeight` ranges between 0 and 1. If `.fractionalHeight` is 1, then it will occupy 1x height.

```
NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1.0))
```
 
* **.absolute**: If we want to have a fixed value, we will use `.absolute`.

```
NSCollectionLayoutSize(widthDimension: .absolute(50.0), heightDimension: .absolute(50.0))
```
 
* **.estimated**: It is used with self-sizing groups and items. We can provide an estimated value. The actual value will be computed during layout.

```
NSCollectionLayoutSize(widthDimension: .estimated(50.0), heightDimension: .estimated(50.0))
```
 


### Comparison


This is a starter project to demonstrate the differences between Traditional layout approach and Compositional layout approach. It might seem that there are no significant differences between both the approaches. But the difference is realised,


1. When we have more complex designs. So, if we modify our current requirements and wish to have a different layout in every section. For the Traditional approach, we will create new class for every `UITableViewCell` and handle multiple data sources. Whereas, in the Compositional layout, we can customise and add more sections *(see 3A, 3B below)*, without the need to add new data sources.

2. When we have more customisation. There are ways in which groups can handle different types of items. The sections can have different scrolling behaviors. Supplementary views to the sections can be created without the overhead of new `UITableViewCell` for `HeaderCell`.

3. Compositional layout approach takes lesser implementation time as compared to Traditional layout approach for complex designs. 

4. Compositional layout requires lesser number of new classes and lesser lines of code.

5. Compositional layout is easy to customise and maintain since the code is not distributed and nested in multiple classes and multiple data sources.

6. Last but not the least, Compositional layouts will lead to developer happiness.

```
// 3A - Create Compositional Layout with multiple sections
func createLayout() {
    let layout = UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
        switch section {
        case 0: return self.firstLayoutSection()
        case 1: return self.secondLayoutSection()
        default: return self.defaultLayoutSection()
        }
    }
    collectionView.collectionViewLayout = layout
}
```
```
// 3B - Create a section with items and group
func firstLayoutSection() {
    let size = NSCollectionLayoutSize(widthDimension: .absolute(250.0), heightDimension: .absolute(150.0))
        
    // Item
    let item = NSCollectionLayoutItem(layoutSize: size)
        
    // Group
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])
        
    // Section
    let section = NSCollectionLayoutSection(group: group)
    section.orthogonalScrollingBehavior = .continuous
        
    // Header in section
    let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.99), heightDimension: .absolute(40.0))
    section.boundarySupplementaryItems = [.init(layoutSize: headerSize, elementKind: "HeaderKind", alignment: .topLeading)]
        
    // Layout
    collectionView.collectionViewLayout = UICollectionViewCompositionalLayout(section: section)
}
```

### Conclusion

In conclusion, I would like to say that if we want to create complex designs and make our app stand out with creative layouts, `UICompositionalLayout` is the way to go.


I have linked a demo project [here](https://github.com/stutid/CompositionalLayout), which creates a layout as below. You can also have a look at [WWDC 2019 Advances in Collection View layout session](https://developer.apple.com/videos/play/wwdc2019/215/). Happy Coding! 😊

![Demo project layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kjh2t6wdpvyrhx8ydh2f.gif)