A still image of transition from list to grid layout.

How to Create Grid Layout in UIKit

There are plenty of ways to present collections of data on the screen to users, with one of the most common layout pattern being a good old-fashioned grid. In UIKit grids are built with a combination of a collection view and its layout object. 

Without further ado, let’s begin.

What’s Compositional Layout

In UIKit, collection views are built with the UICollectionView type. Each data entry in a collection view is displayed within a cell. The cells are arranged by the collection view with the help of a layout object, represented as a subclass of the UICollectionViewLayout class. The layout object is data agnostic, meaning that it doesn’t have any access to the data in the cells whatsoever. The only responsibility of UICollectionViewLayout is to arrange the cells and set spacing between them.

While you could theoretically subclass UICollectionViewLayout to create custom layouts (e.g., arranging items in a circle), most common layouts can be achieved more easily with UIKit’s UICollectionViewCompositionalLayout. The UICollectionViewCompositionalLayout class, itself a subclass of UICollectionViewLayout, is used to compose layouts out of progressively smaller components—sections, groups, and items—much like the layers of a Russian doll fitting one within another.

Image of schematics of the UICollectionViewCompositionalLayout in UIKit.

The smallest component is an item, which represents a single cell in the collection view. Items are then arranged into groups, groups into sections, which in turn make up the layout. Thus, to create a UICollectionViewCompositionalLayout you’ll need to:

This workflow is the same regardless of the layout you want to create. The changes between layouts are expressed in values you pass when composing a layout, but the methodology is the same. 

In this guide I’ll show you how to create a simple grid layout using the UICollectionViewCompositionalLayout.

Image of UICollectionViewCompositionalLayout
This is the grid layout that we’ll be creating in this article.

I. Create Items

Being the smallest part of the compositional layout, items are defined with the NSCollectionLayoutItem type. Each item sets the size and the spacing of a single cell in the collection view. 

The size of an item in compositional layout is expressed with an instance of NSCollectionLayoutSize that takes a pair of NSCollectionLayoutDimension dimensions to set the width and height of the item. The NSCollectionLayoutDimension type comes with a bunch of convenient type methods for creating size objects easily, which we’ll be using throughout the article. 

To create an instance of the NSCollectionLayoutItem pass in the NSCollectionLayoutSize in its init(layoutSize:) initializer. In case of a grid layout, I find it useful to set the size of an item to a fraction of the width or height of the group that contains the item. For example, to create resizable square grid item set both width and height to a fraction of group’s width using the fractionalWidth(_:) method: 

// One third of the group’s width.
let itemDimension = NSCollectionLayoutDimension.fractionalWidth(1.0 / 3.0)

// Item’s width and height are the same to create a square.
let itemSize = NSCollectionLayoutSize(widthDimension: itemDimension, heightDimension: itemDimension)

// Initialize item by passing in the item size.
let item = NSCollectionLayoutItem(layoutSize: itemSize)
Image showcasing the UICollectionViewCompositionalLayout described in this article.
This is the layout described in this article. By the end of this guide you should be able to create a layout like this yourself.

Item Spacing

Once you create an instance of the NSCollectionLayoutItem, you can access its contentInsets and edgeSpacing properties that you can use to set the spacing inside and outside of the item respectively.

let inset: CGFloat = 10

// Add spacing INSIDE the item
item.contentInsets = NSDirectionalEdgeInsets(top: inset, leading: inset, bottom: inset, trailing: inset)

// Add spacing OUTSIDE the item
item.edgeSpacing = .init(leading: .none, top: .none, trailing: .fixed(inset), bottom: .none)
Image showcasing how setting the contentInsets will shrink the items, while setting edgeSpacing will maintain the size of the item, but will push content around it.
UICollectionViewCompositionalLayout
Setting the contentInsets will shrink the items, while setting edgeSpacing will maintain the size of the item, but will push content around it.

II. Arrange Items in a Group

In compositional layout, groups arrange the items in relation to each other and are described with the NSCollectionLayoutGroup type. When you create a group, you choose how the group will arrange the items—horizontally or vertically.

To create a group, call one of the type methods of the NSCollectionLayoutGroup, passing in the size and the items it should contain. For example, for grid layout, groups can arrange items horizontally taking the full width of the screen with fractionalWidth(_:) dimension and as much height as necessary to fit all the items with the use of the estimated(_:) height dimension.

// Full width
let groupWidth = NSCollectionLayoutDimension.fractionalWidth(1.0)

// The 100 points is an estimate. Actual height might be adjusted during render time to fit all items
let groupHeight = NSCollectionLayoutDimension.estimated(100)

// Create group size
let groupSize = NSCollectionLayoutSize(widthDimension: groupWidth, heightDimension: groupHeight)

// Create horizontal group with the items
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

Grids with Nested Groups

The neat thing about the NSCollectionLayoutGroup type is that it’s a subclass of the NSCollectionLayoutItem class, enabling the use of nested groups. Simply pass a group as one of the items to another group and, voila, you got yourself a nested group.

// Large items take 70% of the main group's width and fill the group’s full height.
let largeItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(1.0))
let largeItem = NSCollectionLayoutItem(layoutSize: largeItemSize)

// Small items fill full width of the nested group and half of its height to fit 2 items vertically.
let smallItemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.5))
let smallItem = NSCollectionLayoutItem(layoutSize: smallItemSize)

// Nested group takes the remaining 30% of the main group's width and arranges items vertically.
let nestedGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.3), heightDimension: .fractionalHeight(1.0))
let nestedGroup = NSCollectionLayoutGroup.vertical(layoutSize: nestedGroupSize, subitems: [smallItem])

// Main group fills the screen horizontally and fits 3 main groups vertically
let mainGroupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.3))
let mainGroup = NSCollectionLayoutGroup.horizontal(layoutSize: mainGroupSize, subitems: [largeItem, nestedGroup])
Image showing the nested groups example in UICollectionViewCompositionalLayout
The cool thing about groups is that they act as regular items, allowing you to create truly elaborate layouts with nested groups.

Spacing Between Items in the Group

Groups enable you to set the spacing between items along the group’s orientation axis. To add spacing between items in a group, set the interItemSpacing property of the group to an instance of the NSCollectionLayoutSpacing type, which can be initialized using one of its type methods:

  • fixed(_:) — allows you to set fixed spacing between items, effectively shrinking items horizontally or vertically along the group’s direction;
  • flexible(_:) — sets the spacing to or greater than the amount, filling the available space.
// Fixed spacing example
group.interItemSpacing = .fixed(10)

// Flexible spacing example
group.interItemSpacing = .flexible(10)
Inter item spacing in UICollectionViewCompositionalLayout
Inter item spacing shrinks the items along the group’s axis (horizontally or vertically).

III. Create section with the Group

Sections divide compositional layout into distinct parts and are represented with the NSCollectionLayoutSection type. You create a section with a group, while the properties of the group define how the section displays the content, enabling independent item arrangements for different sections.  

To create a section, pass your group in the init(group:) initializer of the NSCollectionLayoutSection type:

let section = NSCollectionLayoutSection(group: group)

Section Spacing

Once you create a section, you can configure the spacing inside the section and around it by setting the following properties:

  • interGroupSpacing — this property accepts a simple CGFloat and sets the spacing between groups inside the section;
  • contentInsets — analogous to the similar property in items, this property accepts an instance of the NSDirectionalEdgeInsets type, allowing you to set the spacing separate for each side around the section.
// Spacing between groups
section.interGroupSpacing = 10.0

// Padding around section
section.contentInsets = NSDirectionalEdgeInsets(top: 10.0, leading: 20.0, bottom: 10.0, trailing: 20.0)
Image showing NSCollectionLayoutSection properties in action: interGroupSpacing and contentInsets.
You can set the spacing between groups with interGroupSpacing and the padding around the section as a whole with contentInsets.

IV. Create and Apply Layout

Finally, with all the bits and pieces of the layout ready, it’s time to compose it. To create UICollectionViewCompositionalLayout use its init(section:) initializer, passing in the section you configured.

let layout = UICollectionViewCompositionalLayout(section: section)

To apply the layout to the collection view, assign your layout to the collectionViewLayout property of your view:

collectionView.collectionViewLayout = layout

In case you would like to apply different layouts during runtime with animations, for example in response to a device orientation change, you could call the setCollectionViewLayout(_:animated:) method on your collection view, passing in the desired layout:

collectionView.setCollectionViewLayout(layout, animated: true)
You can animate layout change by setting your collection’s layout with setCollectionViewLayout(_:animated:). You can check the implementation shown in this video in the sample project I created for this article on GitHub.

Grid Layout for UICollectionViewController

Below is the complete code to configure a grid layout for a UICollectionViewController—simply call this function in the viewDidLoad of the view controller:

func configureGridLayout() {
    let spacing: CGFloat = 10
    let itemsPerRow: Int = 3
    
    // I. Create item.
    let itemDimension = NSCollectionLayoutDimension.fractionalWidth(1.0 / Double(itemsPerRow))
    let itemSize = NSCollectionLayoutSize(widthDimension: itemDimension, heightDimension: itemDimension)
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    // II. Create a group with the item.
    let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(10))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
    group.interItemSpacing = .flexible(spacing)
    
    // III. Create section with the group
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = spacing
    section.contentInsets = NSDirectionalEdgeInsets(top: spacing / 2, leading: spacing, bottom: spacing / 2, trailing: spacing)
    
    // IV. Create and apply layout.
    let layout = UICollectionViewCompositionalLayout(section: section)
    collectionView.collectionViewLayout = layout
}

Wrap Up

Well, there you have it. In this guide we’ve covered how to create grid layouts using the UICollectionViewCompositionalLayout, but you can use this workflow to create any layout. You may also add headers and footers to sections and decorations to individual items using the NSCollectionLayoutSupplementaryItem class, however that’s worth another article on its own.

I hope you found this guide useful. Happy composing!

Discover more from Dudee

Subscribe now to keep reading and get access to the full archive.

Continue reading