close

Somewhere, in a parallel universe,
the-other-you just clicked on
"Subscribe To Our Monthly Newsletter"

The-other-you seems eager to stay updated with the technological changes.

Please enter your name.
Please enter your email. Please enter your email.

How To Use Compositional Layout In Collection View?

Post by|iOS App22 October,2020
400 View

Introduction

Since iOS6, many developers have often used collection views and built custom layouts by subclassing UICollectionViewLayout or UICollectionViewFlowLayout classes for iOS app development. If you look at the AppStore or iOS Photos app, it’s very tough to develop such layouts with UICollectionViewFlowLayout, with multiple scrolling sections and variably sized tiled layouts.

I spent many hours getting the desired output for all iOS versions, custom iPhone app development, and on every device. So, if you are struggling with building out complex collection view layouts, then your wait is over. Through this blog, I will explain how to build such complex layouts with the brand new UICollectionViewCompositionalLayout introduced by Apple at the WWDC 2019. Using this layout, you could build complex layouts with few code lines, but one thing you should know is that it only supports iOS 13 or above. We are going to build the below layout:

Your browser do not support HTML5 video.

 

I’ll first show you the basic core components of the layout and how it works under the hood, and from that, we will work towards our result.

Compositional Layouts: Core Components

The compositional layout is made up of four basic building blocks, and it’s a hierarchy that consists of:

  1. Item
  2. Group
  3. Section
  4. Layout

If we look at the above image, you can see that this is the new layout for the collection view. The utmost layer is the layout. The layout may have sections. In that section, it may have groups, and groups may have items.

Here, I’m going to assume that you already set up the following things:

  1. CollectionView (Using storyboard or programmatically)
  2. Respective data source and delegate methods

Now you know the hierarchy of compositional layout. The next step is to build the layout.

Our layout is divided into four sections. I would strongly recommend putting the compositional layout code in a method that I’ll demonstrate soon. You can then assign the layout to your collection view using the following code that you need to place in your viewDidLoad() method:

collectionView.collectionViewLayout = createCompositionalLayout()

As I mentioned earlier, we need to create four sections to achieve our desired layout. I’ll explain the anatomy of a compositional layout using a code example of each section. First of all, let’s create that compositional layout method having four sections.

private func createCompositionalLayout() -> UICollectionViewCompositionalLayout {

    return UICollectionViewCompositionalLayout { (sectionNumber, env) -> NSCollectionLayoutSection? in

     switch sectionNumber {

     case 0: return self.firstLayoutSection()
     case 1: return self.secondLayoutSection()
     default: return self.thirdLayoutSection()
     }
   }
}

If you look at the code above from the return statement, you will notice that UICollectionViewCompositionalLayout takes an initializer of NSCollectionLayoutSection. Thus layout is built using sections. Let’s create our first layout section.

First Section

private func firstLayoutSection() -> NSCollectionLayoutSection {

   let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: 
.fractionalHeight(1))

   let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets.bottom = 15

   let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.9), heightDimension: 
.fractionalWidth(0.5))

   let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
group.contentInsets = .init(top: 0, leading: 15, bottom: 0, trailing: 2)

   let section = NSCollectionLayoutSection(group: group)

   section.orthogonalScrollingBehavior = .groupPaging

   return section
}

As you can see in the code above, that section is built using NSCollectionLayoutGroup. A single section can contain multiple groups. We will do most configuration work on groups as they are considered the most significant workhorse of a compositional layout.

In this example code, we are using NSCollectionLayoutGroup.horizontal to create the layout group. By making it horizontal, all added items to the group will be placed horizontally in the layout. This means that the group will position items starting from the X-axis to the group’s defined width, and then it starts setting things in the next line.

Size Classes

There are four size options that we can use according to our needs, and that will eliminate the need for the calculation to achieve the desired output. Let’s see all the size classes:

  1. fractionalWidth(): Using fractionalWidth we can set the width/height of the cell proportional to its parent’s width
  2. fractionalHeight(): Using fractionalHeight, we can set the width/height of the cell proportional to its parent’s height
  3. absolute(): Using absolute value, we can set the width/height of the cell to a fixed value
  4. estimate(): Using estimate value, we can set the cell’s width/height according to the content size. The system will decide the optimal width/height for the content.

Adjusting The Spacing Between Items, Groups, And Sections

There are two possible ways to give space to collection view cells:

  1. Give edge insets on the items, groups, and sections.
  2. Give spacing between individual items and groups.

When you apply an edge insets to an item, the size of the item doesn’t change. Please see the below image:

In the above image, you can see that insets are applied inward from the bounding box, and the cell is eventually rendered in the remaining space.

item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

Try adding the above line of the code, and you will see the change. We can also apply the same insets to groups and sections.

Let’s define the size of our group and its items. You might notice that I have declared fractionalWidth(0.9). The value of fractionalWidth or fractionalHeight should be between 0 to 1. We don’t want the group’s full width, but we want a bit gap here, and we want the height half of the screen. So I have defined fractional width 0.9 and fractional height 0.1. For the item size, we need the item to take the full width and height of its container, so I’ve defined fraction width and height 1.0. Lastly, to achieve scrolling slider behavior, we need to set the orthogonalScrollingBehavior to groupPaging.

You will get below output when you run your code.

Second Section

private func secondLayoutSection() -> NSCollectionLayoutSection {

   let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.33), 
heightDimension: .absolute(100))

   let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = .init(top: 0, leading: 0, bottom: 15, trailing: 15)

   let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), 
heightDimension: .estimated(500))

   let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

   let section = NSCollectionLayoutSection(group: group)
 section.contentInsets.leading = 15

   section.boundarySupplementaryItems = [
NSCollectionLayoutBoundarySupplementaryItem(layoutSize: .init(widthDimension: 
.fractionalWidth(1), heightDimension: .estimated(44)), elementKind: categoryHeaderId, alignment: 
.topLeading)
]

 return section
}

In the second section, we need to divide the grid into three columns. fractionalWidth(0.33)will divide the items in 3 columns and absolute(100) will set fixed height to 100 points. Declare group size with fractionalWidth(0.1) full width of its parent and estimated(500) because we need a system to decide height based on content.

Add Section Headers To A Layout

Our second layout contains a doubleheader. Let’s see how to add a section header to our compositional layout if you follow the second section’s code. The header is defined as an instance of NSCollectionLayoutBoundarySupplementaryItem. The alignment argument has different attributes like a top, bottom, topLeading, bottomLeading, etc. Based on the alignment value you pass, it will act as a header or footer. Also, there’s an elementKind argument. It is just a string identifier, similar to a cell reuse identifier.

Note

As I mentioned initially, this post is not about setting up a collection view data source. So I’m not going to explain the section header in detail, but you will find the code in the demo project link that you will find at the end of this post. You need to implement this method:

collectionView(_:viewForSupplementaryElementOfKind:at:)

You also need to register a UICollectionViewCell subclass on your collection view using the

register(_:forSupplementaryViewOfKind:withReuseIdentifier:) method.

The following image shows our progress so far:

Third Section

private func thirdLayoutSection() -> NSCollectionLayoutSection {

   let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: 
.fractionalHeight(1))

   let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets.bottom = 15

   let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.8), 
heightDimension: .fractionalWidth(0.35))

   let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
group.contentInsets = .init(top: 0, leading: 15, bottom: 0, trailing: 2)

   let section = NSCollectionLayoutSection(group: group)

   section.orthogonalScrollingBehavior = .continuous

   return section
}

The last section is somewhat similar to our first section. The only difference is in the height and width of the item and the scrolling behavior. For the continuous scrolling behavior, we need to set the orthogonalScrollingBehavior to continuous.

Run your code, and now we got what we expected:

There’s More

I’ve shown you how to build a photo album like a basic layout with an example. We can even create more complex and exciting layouts using horizontal, vertical, and custom groups, badges in our compositional layout. You can also watch the WWDC 2019 video for better understanding.

I would strongly recommend using Diffable Datasource introduced at WWDC 2019, and it works excellent with compositional layouts as it uses type-safe identifiers to identify its items and sections.

That wraps up this piece. Here is the GitHub link of the complete source code. If you have any questions about this article or built something cool with the compositional layout, let me know in the comment box. I love to hear from you.

Also, if you’d like to hire an iPhone developer or get in touch with an iPhone app development company, you can always rely on ZealousWeb to help you with your development needs.

FAQ

What is the use of orthogonal scrolling behavior property in the compositional layout?

When you assign a value to the orthogonalScrollingBehavior property on a section, it flips everything on its top. The section will place next to the group, and since we have a single item in the group, we can achieve a carousel effect as we have in the first and last section of the demo. 

Can we pass more than one item to NSCollectionLayoutGroup’s subitems array?

Yes, We can pass more than one item as an array. 


Speak Your Mind

  1. Eve says:

    What is ContentView.swift for? Just a SwiftUI test?

    Please enter your comment.
    Please enter your name.
    • Kevin says:

      Yes, Just for a test purpose. You can ignore it. Thanks!

      Please enter your comment.
      Please enter your name.
Please enter your comment.
Please enter your name.