How to create a compositional layout using collection view in swift

Jalpesh Goti
6 min readMay 7, 2021

Introduction

We are developing a collection view with a custom design build-up using of UICollectionviewFlowLayout or using UICollectionviewLayout classes for the iOS app development. If you are seeing the app store design that creates using an UICollectionviewFlowLayout is tough to implement with multiple sections and each section manages scrolling behavior as well as layout.

I have done many hours to get the desired output for all ios versions and custom iOS application development. So I will explain how to build up a compositional layout using a single UICollectionviewCompositionalLayout. Using this layout, You could build a complex layout with few lines, but you should know that it supports iOS 13 or above.

So let's start UICollectionviewCompositionalLayout….

I will show you the basic concept of the layout, Section, Item, Group, and how it works with the UICollectionviewCompositionalLayout.

Core components of the layout :

The compositional layout is made with four-component and its hierarchy that consist of :

  1. Item
  2. Group
  3. Section
  4. Layout
Core Components structure

If we look above component structure, it's the new layout of the collection view. we look at the hierarchy of view is the first layer is the layout after that section, it may have a group, and group may have items.

So Let start the development, I assume that you have set up two things in the project.

1. Collection View(Using storyboard OR programmatically).

2. Data source and delegate method.

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

  • You can assign the layout to your collection view using the below code and put them in the viewDidLoad() method.
  • Make sure here collePopular is the outlet of the collection view.
self.collePopular.collectionViewLayout = createCompositionalLayout()

We need to create a four-section to achieve a goal. (Output 😊). I will explain a compositional layout using the code of each section. First of all, let’s create a compositional layout method having four sections.

private func createCompositionalLayout() -> UICollectionViewCompositionalLayout {return UICollectionViewCompositionalLayout { (sectionNumber, env) -> NSCollectionLayoutSection? inswitch sectionNumber {case 0: return self.firstLayoutSection()case 1: return self.secondLayoutSection()case 2: return self.thirdLayoutSection()default: return self.fourthLayoutSection()    }  }}
  1. Create the first Section.
private func firstLayoutSection() -> NSCollectionLayoutSection {let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))let item = NSCollectionLayoutItem(layoutSize: itemSize) // Whithout badgeitem.contentInsets = .init(top: 15, leading: 0, bottom: 15, trailing: 0)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 = .groupPagingreturn section}

As you can see in the above code, that section built using the NSCollectionLayoutGroup. A single section contains multiple groups. In this, we are using NSCollectionLayoutGroup.horizontal to create the layout group with a scrolling direction of horizontal. All added items to the group will be placed horizontally in the layout. The group will position items starting from X-axis to groups define width and height using four types of size classes.

Size classes

There is four size class that we can use according to our needs. Let’s see all size classes.

  1. fractionalWidth(): We can set the width of the cell proportional to the super view width using the fractionalWidth.
  2. fractionalHeight(): We can set the height of the cell proportional to the super view height using the fractionalHeight.
  3. absolute(): We can set the width/height of the cell to a fixed value.
  4. estimate (): We can set the width/height of the cell according to the content size.

Adjust the spacing between item, group, and section.

There is two possible way to give space to a cell.

  • Give EdgeInsets on the items, group, and section.
  • Give spacing between individual items and groups.
item.contentInsets = .init(top: 15, leading: 0, bottom: 15, trailing: 0)

You can manage the EdgeInsets of your requirement of mockup. Try any content Insets change to see what is happened in the layout. We can add the same content insets to the groups and sections.

Let's define the size of our group and its items. You might be noticed declared fractionalWidth(1). The value of fractionalWidth is between 0 to 1. We don't want the group full width but we want some space here, and we want some height so I have to define fractionaWidth and fractional height. I have to define fractional width and height 1.0 to get the full width and height of its container, for scrolling behavior we add orthogonalScrollingBehavior to groupPaging.

Let’s see out of the first section. 👇🏻👇🏻👇🏻

First Section Output.

Second Section

private func secondLayoutSection() -> NSCollectionLayoutSection {let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.50),heightDimension: .fractionalHeight(1))let item = NSCollectionLayoutItem(layoutSize: itemSize)item.contentInsets = .init(top: 0, leading: 0, bottom: 15, trailing: 15)let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),heightDimension: .estimated(200))let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])let section = NSCollectionLayoutSection(group: group)section.contentInsets = .init(top: 0, leading: 0, bottom: 15, trailing: 0)section.contentInsets.leading = 15section.orthogonalScrollingBehavior = .continuoussection.boundarySupplementaryItems = [NSCollectionLayoutBoundarySupplementaryItem(layoutSize: .init(widthDimension:.fractionalWidth(1), heightDimension: .estimated(44)), elementKind: categoryHeaderId, alignment:.topLeading)]return section}

In the second section, same as the first section, but a little bit changes look at above code to you know some changes as per the first section. I have changes items width by using size classes of fractionalWidth(0.5) and fractionalHeight(1) and look at the group size to set estimated(200). Also, set the scrolling behavior orthogonalScrollingBehavior with a value of continues.

More important is the section header which is you can create your own XIB or programmatically set with using NSCollectionLayoutBoundary -SupplementaryItem and you guys very smart to know how to register XIB in the viewDidLoad method 😊. Also, implement the collection view data source method viewForSupplementaryElementOfKind.

collectionView(_ collectionView:,viewForSupplementaryElementOfKind kind:, at indexPath: )

Let’s see the second section. 👇🏻👇🏻👇🏻

Second Section Output.

Third Section

private func thirdLayoutSection() -> NSCollectionLayoutSection {let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),heightDimension: .fractionalHeight(1.0))let item = NSCollectionLayoutItem(layoutSize: itemSize)item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),heightDimension: .absolute(100))let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,subitem: item, count: 2)group.interItemSpacing = .fixed(CGFloat(10))let section = NSCollectionLayoutSection(group: group)section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 15, bottom: 0, trailing: 15)section.boundarySupplementaryItems = [
NSCollectionLayoutBoundarySupplementaryItem(layoutSize: .init(widthDimension:.fractionalWidth(1), heightDimension: .estimated(44)), elementKind: categoryHeaderId, alignment:.topLeading)
]
return section
}

In this section, here is different from the first & second section so, If you make the layout 2 by 2 you will be managed height and width as per parent view by using fractional width/height. Also, you can pass how many cells you want in one row, If you want two cells per row then you can use NSCollectionLayoutGroup.horizontal with an argument of the count.

let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,subitem: item, count: 2) // 2,3 etc..

Let’s see the third section. 👇🏻👇🏻👇🏻

Third Section Output.

Fourth Section

private func fourthLayoutSection() -> NSCollectionLayoutSection {let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),heightDimension: .fractionalHeight(1.0))let item = NSCollectionLayoutItem(layoutSize: itemSize)item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),heightDimension: .absolute(100))let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 4)let section = NSCollectionLayoutSection(group: group)section.contentInsets = NSDirectionalEdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 15)return section}

The only difference of height, width, and number of counts with the comparison of the third section.

Run your code, and how we got the result.👇🏻👇🏻👇🏻

Fourth Section Output.

Also more…

I have shown you how to build a compositional layout like a gallery/album like a basic layout. We can even create a more complex layout using horizontal, vertical, and custom groups, add new badges in our layout. You can also watch the WWDC 2019 event video for a better understanding.

Also, If you had like to hire an iPhone developer or get in touch with a profile about.me.

Thanks for reading…… 🤝👍🏻

--

--