UICollection Compositional Layout Part 1

Apple introduced Collection View in iOS 6, In previous blogs we created layout using ios 6 API. In iOS 13 , they are supercharged with the addition of Compositional layout

Compositional layout →, a type of collection view layout that’s composable (from small bits of layout), flexible, and fast, letting you build any kind of visual arrangement for your content. It’s declarative api. In ios 13 app store and share sheet re-architecture using compositional layout, With Compositional layout you get RTL support for free and no code for paging behaviour for RTL language

In this part we created a grid layout using UICollectionViewFlowLayout , Now it’s time to build the same layout with compositional layout

Collection View Layout

Compositional Layout consists of three layout parts

Item

  1. It is the smallest unit in the hierarchy and represent an individual piece of data that you want to display on screen
  2. An item ultimately live inside of cell

Group

  1. Item live inside group
  2. Group represents row
  3. It is the basic unit of layout , Group specify the direction in which data is laid out and can be composed together to create a more complex layout

Section

  1. A section simply a grouping of data and corresponds how the data is organized in the data source
  2. Collection View can have multiple sections each containing it’s own groups and items
Figure 1

In ios 6 , The APIs that comprise UICollectionView can be slotted into three distinct categories — Data, Layout and Presentation. One of the novel concepts UICollectionView was built on was the separation of concerns between the data, or the “what,” from the layout, the “where” content is being rendered. This distinction is at the core of what makes UICollectionView so flexible. When UICollectionView was first released back in iOS 6, data was managed via an indexPath-based protocol, UICollectionViewDataSource. For layout we had an abstract class, UICollectionViewLayout, and we shipped a concrete subclass, UICollectionViewFlowLayout. And on the presentation side, we published two view types — UICollectionViewCell and UICollectionReusableView.

Figure 2

in iOS 13, we introduced two new components for Data and Layout respectively with Diffable Data Source and Compositional Layout. These are APIs to use for building apps with UICollectionViews today.

Figure 3

Getting Started

As shown in Figure 4, we created this layout using compositional layout, Let’s discuss how we created this layout in next section

Figure 4

Step 1 Collection View :

Created a collection view and pin it to the screen using, also created custom cell that have single label where we show the number we will not go in details

Figure 5

Step 2 Create Layout :

As shown in Figure 6, we define layout configuration for this collection view .

  1. First we assign collectionViewLayout property of collection view by returning it to UICollectionViewCompositionalLayout which is subclass of the UICollectionViewLayout abstract class, Plz note UICollectionViewCompositionalLayout only available from code not like you can configure FlowLayout from storyboard

Items

  1. First as discussed in Collection View Layout, we need to create Item , To create an item , we create an instance of NSCollectionLayoutItem
  2. These items don’t represent the actual item , think of it as a blue print that is used when the actual data is provided , It’s stored the layout information of the UICollectionViewCell
  3. When creating an item you need to provide size , Now you might think it should be a floating point integer but it’s not the case. You need to provide size using NSCollectionLayoutSize class
  4. NSCollectionLayoutSize allows us to define width and height as an instance of NSCollectionLayoutDimension, Note: size is not scalable means float , int instead it has separate class called NSCollectionLayoutDimension
  5. ItemNSCollectionLayoutItemNSCollectionLayoutSizeNSCollectionLayoutDimension

NSCollectionLayoutDimension

It allow you to define measurement as fractional , absolute or estimated value. It is an axis independent way to describe how big a particular axis is , we have four different variations Absolute, Estimated, Fractional Width and Fractional Height. You define layout size dimension relative to its parent

Absolute → The item will have the exact width or height value specified will be used

Estimated → Is useful when dealing with item that display content . By providing an estimated size , you allow the collection view to initially draw the item which can then be adjusted to the size of the content which is very helpful, It allows you to do per axis cell sizing , Height dimension estimated so as content rendered layout have better idea , it invalidate layout automatically which is very easy to support dynamic type we will cover this detail in later part

Fractional Width and Height → It allows you to specify a value as a fractional component of its container for Item parent will be group.

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

Width = FractionWidth * 0.2 indicating the width of the item to be 20% of the width of the group and height of item will be 100% of the group height

For group / row we are specify width of group is the 100% width of it’s parent which is collection view itself which is taking whole screen , and height of the group will be 20% of the width of it’s parent which is again collection view, So now you are thinking we can do lots of things

Now we created item we need to add it into the group

Groups

  1. NSCollectionLayoutSize is a flexible class and you can use it to specify the size of the group as well, Since the sizes are specified either as absolute or as a size relative to its container the collection view resolves the sizes at run time
  2. In this case Group width = 1.0 super view which is section and section parent is collection view layout so it will take whole width of screen, and height = 0.2 * width of it’s super view which is again section which we will discuss in next section. Note we define height w.r.t width of parent this is what we called define size independent of axis
  3. Groups are instance of NSCollectionLayoutGroup and must specify the axis where to do scrolling, size and items it contains , we will discuss later subitems: [item]) , to be simple for now
  4. Group represents row, so in this case each group or row contains 5 item and height of group. equal to width of individual item
  5. Group is basic unit of layout that we can compose together . they have three. forms horizontal , vertical and custom which we discuss later. you can think of little many flow layouts. They are on line like horizontal or vertical . Custom group that allow you to specify absolute size and position of item in a custom way . you can do custom group alog side with horizontal and vertical
  6. A group normally have repeating structure

Section

  1. A collection view layout has one or more sections. Sections provide a way to separate the layout into distinct pieces.
  2. Each section can have the same layout or a different layout than the other sections in the collection view. A section’s layout is determined by the properties of the group (NSCollectionLayoutGroup) that's used to create the section.
  3. Each section can have its own background, header, and footer to distinguish it from other sections.
  4. Section is an instance of NSCollectionLayoutSection, where we specify group under this section and finally UICollectionViewCompositionalLayout we pass section, you are thinking here how can we specify different section layout , so trust be we will cover this later
Figure 6
https://lickability.com/blog/getting-started-with-uicollectionviewcompositionallayout/#:~:text=Sections%20and%20items%20described%20in,our%20items%20within%20a%20section.

Item Size Calculation

let say collection view width = 428, screen width = 428

We define layout how item is calculated we discuss in this section

  1. You have an item you want to display on screen
  2. Section → Group → Item
  3. By default section will fill up the entire width of the screen in this case since collection view width took whole screen width
  4. For height of section , section relies on nested container to inform its size
  5. Section contains group so next group goes in here
  6. Group width is same section , so group width will be 428
  7. Group height = 0.2 * width of section = 85.7 is the height of group
  8. Now finally item item width = 0.2 & width of group = 85.7
  9. Item height = 1.0 height of group which is same as group = 85.7
Figure 7

Step 3 Configure DataSource :

  1. Collection view didn’t organise and manage underlying data either . this is the responsibility of data source object
  2. Datasource manages the data
  3. Provides the collection view with snapshot of the data to display
  4. Let’s say our data consisted of a series of names that we wanted to display in our collection view
  5. We started with our initial snapshot , A snapshot is considered the truth of the current UI state (This one is very important )
  6. We provide the initial snapshot to the data source along with instruction on what to do with the data and it handles the rest
Figure 8
  1. The datasource takes each value from the snapshot we provided and applies it as shown in Figure 9
  2. It asks the collection view for a cell and assign the data to the designated spots inn the cell
Figure 8

But datasource does more than that ,

  1. let’s say collection has a sort button and we sorted the data alphabetically , This is the second snapshot of our data , we hand it back to data source and ask it to display same data but organized in a different manner
  2. The data source can automatically figure out the difference or the diff between two snap shots and instead of simply populating the collection view with new data , it tells the collection view how to move the existing data around to present it how we want
  3. Because the data source can diff between two snapshots, it is called the diffable data source
  4. Diffable data source are declarative approach to dealing with underlying data (Rather than telling the data source how to move data around with declarative approach, we simple tell the datasource what the new state of data is) The data source automatically does the job of figuring out the difference between old and new state and how to apply those changes ti our collection view
  5. The only requirement for this to work is that in your data set you provide the data source each value must have a unique identifier . In this simple app we are showing only rows of number , you can use number itself as an identifier as long as each number os different the value will be unique
Figure 9
  1. In code diffable data source are defined as instance of UICollectionViewDiffableDataSource class when creating an instance , you specify what type of data it will contain along with instructions on how to populate a cell
  2. Then we create a snap shot of the data using the NSDiffableDataSourceSnapshot class
  3. As shown in Figure 11 we used UICollectionViewDiffableDataSource to create a datasource object , you need to maintain it as a strong reference to the datasource so we defined it as stored property
  4. UICollectionViewDiffableDataSource is a generic class that takes two parameter Section type and Item type as shown in Figure 10 both SectionIdentifierType and ItemIdentifierType has single constraint that they both need to conform to Hashable , The hashes provided by these types are used as the unique identifiers by the datasource . This is really useful and in practice means nearly any object can be used to denote section or an item
  5. For our case we only have one section , so we don’t need anything complicated , we can use string identifier to denote a section, but in swift we can use enum , we created enum called Section to define the different sections in the app , we added a section case main that holds all the items in your collection view
  6. Since compiler will synthesise hashable conformance for enums that all you need to do
  7. For Items, since the app is displaying numbers in a list you can specify the item of the type Int, Int already conforms to Hashable so we don’t need to do anything extra
  8. Using these two types we can create an instance of UICollectionViewDiffableDataSource we provide it collection view reference so that it know which collections view to work , second param is closure that defines how the collection view map the data to each cell
  9. The closure takes three argument the collection view being setup , the indexpath of the corresponding cell and the data to present in the cell, the logic you apply will run on every instance of data on the data source, Using this closure you tell the data source how to take any given item of data and present in a cell that collection view provides (Like cellForIndexPath)
  10. Inside closure you dequeue cell and configure cell like old school thing

Apply SnapShot to DataSource(The Truth)

  1. Last thing you need to provide the data source with its initial snapshot of data
  2. Snapshot are instances of NSDiffableDataSourceSnapshot unlike data source it is an generic type of Two parameter , one that define the section and another that define the Item Type var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
  3. Snapshot defines its data in terms of section and items with section contains the series of items
  4. To snapshot you first to add a section using the appendSections method , This method takes an array of section matching the type specify in the data source and snapshot type definition , since we have single section you can provide enum value .main as an argument
  5. Next you need to add items to this section we used snapshot.appendItems(<#T##identifiers: [Int]##[Int]#>, toSection: <#T##Section?#>) method, to specify item for each session since we only have one section we used appendItems without section parameter it will use section 1
  6. Now we specify initial snapshot we need to apply to datasource, apply take snapshot and animatingDifferences since we first load whole data we don’t need animation . animatingDifferences (we want animating loading of data)
  7. What is apply mean the date source iterates over the data and passes each number , along with collection view and the indexpath for each cell to the closure we just defined
Figure 10
Figure 11

As shown in Figure 12 I just change item size width to 1.0 now item is taking whole screen width , like a UITableViewCell type layout

Figure 12

interGroupSpacing → The amount of space between the groups in the section as shown in Figure 13

Figure 13

section contentInsets → The amount of space between the content of the section and its boundaries.

NSDirectionalEdgeInsets → Edge insets that take language direction into account.

Figure 14

group.contentInsets → The amount of space added around the content of the item to adjust its final size after its position is computed. content insets to each edge of each item in a group. So you can now thinking we can have lots of variation to add edge insets

Figure 15

we can specify on item level as well , Note order is important as shown in Figure 17 we specify item content inset after we created group in that case it will not work

Figure 16
Figure 17

As you can in see in Figure 18 we created two column based layout . the new thing that you should see is we are constructing group that represents each row in a slightly different way . we used the different form of initialiser that takes explicit count parameter , here we explicitly specifying that we want to have exactly two item per group or two item per row. Now this cause compositional layout to automatically figure out what the item width has to be in order to make that happen . We do specify item width here because we always have to we say 100% of the container but that value at the top ends effectively getting overridden. When you ask for certain number of items per group , compositional layout will override and it gonna compute whatever width is actually necessary to fulfil our request

Figure 18

Useful links

--

--

--

Senior iOS Engineer | HungerStation | Delivery Hero

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

mac Installing Flipper-Glog (0.3.6) [!] /bin/bash -c

Swift New Concurrency Framework (Part 2)

Alternative Sorting of Array in Swift

How to Test Push Notifications on the iOS Simulator

Generics in Swift

10 Flutter tips — season 2 — part 3/10

Xcode invocation tool — xed

8 Pieces of Advice I Wish Someone Gave Me When I Started Working On a tvOS App

Apple tv remote control in front of the Apple tv.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Ali Akhtar

Ali Akhtar

Senior iOS Engineer | HungerStation | Delivery Hero

More from Medium

The UIPickerView Guide (Swift Tutorial)

How to preview UIViewControllers using SwiftUI

From ObjC → Swift → ObjC: Part 1

Swift Operation Queue