UICollection Compositional Layout Part 3

Ali Akhtar
10 min readAug 20, 2021

In previous part we covered the advanced topic of compositional layout . In this part we will be working with supplementary view . Now that we are comfortable with the base implementation, this part of the series will focus on the Supplementary Items and Decorations that we can use in a Compositional Layout and the way to implement them.

CollectionView manages three basic view class types: Your cells, the things you interact with to express your model objects; it also represents supplementary items and decoration items. Now, these are meant to adorn other parts of your layout to give you visual cues about content information, like maybe a badge on a cell that says, “Hey, you got a comment on your tweet,” or whatever.

And we see common uses for these today with these three examples — badges, and headers, and footers. And we have support and flow today with sticky headers and footers. And they float above that content. But we extend this in compositional layout, making this a whole lot easier. And we can simplify this with this notion of being able to anchor content onto an item or group in your layout. It simplifies that visual relationship, how that might work. All right, let’s look at this visually. So here we can see we’ve got this new type, NSCollectionLayoutAnchor.

Create a Badge Supplementary View

As shown in Figure 1

  1. First we define an NSCollectionLayoutAnchor for our badge. By specifying [.top, .trailing] we position our badge in the top right corner of the parent, while the fractionalOffset positions our badge’s center in the the corner. A fractionalOffset of .zero would position the whole badge inside the parent.
  2. As encountered before, we need to create a size for our item, so here we specify that we our badge should have a fixed value for width and height
  3. We initialise our NSCollectionLayoutSupplementaryItem by assigning it the size, the anchor and an elementKind, based upon which we will identify our badge in collectionView:viewForSupplementaryElementOfKind:atIndexPath:.
  4. Now that we have our badge ready, we just need to assign it to our items. To do so just replace NSCollectionLayoutItem initialiser and add supplementaryItems and passed badge

So here we see we have NSCollectionLayoutAnchor we create right away. And we specify the edges. We want this item to be pinned to the top trailing side of that particular cell. And we want it to poke outside of that geometry a little bit. And we see that with fractional height. we see here that items have these little notifiers on them, right, letting them know, And with this, they kind of poke outside that geometry a little bit, right? They’re not really inside the geometry of the cell itself. So that fractionalOffset is what buys us the ability to poke outside a little bit. We’re going to move over fractionally 30% in the positive X and then up in the minus Y 30% as well. And then we see we defined the CollectionLayout SupplementaryItem with a badgeSize and elementKind. So we’re going to refer back to the view class for CollectionView with that registered supplementary type.And then we specify the container’s anchor, specifying how it’s going to relate. So now that we have this definition of our supplementary, we need to associate it with something. And that needs to be associated with an item, a cell. So in this case we’re going to initialize it with an extended variant of initializer that takes an array of supplementaries. And that’s it.

Figure 1
Figure 2

As shown in Figure 3 we moved the badge to top left / leading and fractionalOffset : is the offset from item , so we specify zero and it will anchor badge within the item. Few things to be cover

  1. If we want to tell Compositional Layout we want supplementary views, we need to associate them with NSCollectionLayoutItem or NSCollectionLayoutGroup. This can be done either with init or later with the supplementaryItems property. Both cases expect an array of NSCollectionLayoutSupplementaryItem. This is a class that lets us define the supplementary items. You can’t add NSCollectionLayoutSupplementaryItem item on section. For section we have NSCollectionLayoutBoundarySupplementaryItem which we will see in next section
Figure 3

NSCollectionLayoutAnchor → An object that defines how to attach a supplementary item to an item in a collection view.You use an anchor to attach a supplementary item to a specific item. An anchor contains information about where on the item your supplementary item is attached, including:

An edge or set of edges. You can attach a supplementary item to a single edge, or to a corner by specifying two adjacent edges. An offset from the item. By default, the supplementary item is anchored within the specified edges of the item it’s attached to. You can change this location by providing a custom offset when you create an anchor.

Code for Badge User interface

Figure 4

As shown in Figure 5 we did few things

  1. create a badge , anchor it to item which is cell
  2. Then create a group assign item to a group
  3. Finally we create sectionHeader NSCollectionLayoutBoundarySupplementaryItem , give it layout headerFooterSize and give alignment to header as top and footer bottom. Note headerFooterSize is dynamic which means it will grow as the content increase this is happen because we provide estimated height instead of absolute
  4. Then we supply boundarySupplementaryItems and pass header and footer to section

boundarySupplementaryItems →An array of the supplementary items that are associated with the boundary edges of the section, such as headers and footers.

NSCollectionLayoutSupplementaryItem → An object used to add headers or footers to a collection view. A boundary supplementary item is a specialized type of supplementary item (NSCollectionLayoutSupplementaryItem). You use boundary supplementary items to add headers or footers to a section of a collection view or the entire collection view. Each type of supplementary item must have a unique element kind. Consider tracking these strings together in a way that makes it straightforward to identify each element.

struct ElementKind {
static let badge = "badge-element-kind"
static let background = "background-element-kind"
static let sectionHeader = "section-header-element-kind"
static let sectionFooter = "section-footer-element-kind"
static let layoutHeader = "layout-header-element-kind"
static let layoutFooter = "layout-footer-element-kind"
}
Figure 5
Gif 1

As shown in Figure 6 , Diffable Data Source has supplementaryViewProvider property we can use to provide supplementary views. This can be a closure or you can define method that takes collection view, element kind and indexPath and assign this method.

Figure 6

Register supplementary view is very important otherwise app will crash

Figure 7

As shown in Figure 8 , we pinned header and footer

pinToVisibleBounds → A Boolean value that indicates whether a header or footer is pinned to the top or bottom visible boundary of the section or layout it’s attached to. The default value of this property is false, which means that the boundary supplementary item (header or footer) remains in its original position during scrolling, and moves offscreen as its section or layout scrolls. Set the value of this property to true to pin the boundary supplementary item to the visible bounds of the section or layout it’s attached to. This way, the boundary supplementary item is shown while any portion of the section or layout it’s attached to is visible.

Figure 8

Gif 2 you see header pin at top and footer pin ad bottom but we have one problem our badge view over lap header and footer

Gif 2

As shown in Figure 9 we change zindex

zindex → The vertical stacking order of the supplementary item in relation to other items in the section. This property is used to determine the front-to-back ordering of items during layout. Items with higher index values appear on top of items with lower values. Items with the same value have an undetermined order.

Figure 9

Now we added two badge in each item, note each supplementary item should have unique elementKind Also one thing elementKind could be any string. The below struct is just a template constants provided by apple

struct ElementKind {
static let badge = "badge-element-kind"
static let background = "background-element-kind"
static let sectionHeader = "section-header-element-kind"
static let sectionFooter = "section-footer-element-kind"
static let layoutHeader = "layout-header-element-kind"
static let layoutFooter = "layout-footer-element-kind"
}
Figure 10

DataSource

Figure 11

Register supplementary View

Figure 12
Figure 13

As shown in Figure 14 we anchor yellow badge View to a group and green badge to a item , Note: we can’t do with section.

Figure 14

Decoration Items

In addition to supplementary items, we can customize our section layout with decoration items. This will allow us to easily add backgrounds to our sections. The background view we’ll create is quite simple (a gray rectangle with a corner radius), so we’ll do it in code

All right. So by now you’ve played around with the brand new iOS 13 card presentation, this whole card design language all throughout the system. And we see this in scrolling UI’s as well where all kinds of content is grouped together logically with cards. And this is a natural fit for CollectionView because we always had support for notions of decoration views. Well, in the past you had to do the math yourself. Well, now we’ve made it a lot simpler with compositional layout. And we support this with a CollectionLayoutDecorationItem. You just create it with an element kind and you’re done. This is intended to be used to have a view that’s behind the section content itself to give you that nice visual grouping. And to construct it, there’s just one line of code. And then to add it to the section, you just want to specify the items and off you go

As shown in Figure 15 we created layout for background view that will show for each section and we register with layout

layout.register(BackgroundSupplementaryView.self, forDecorationViewOfKind: “background”) → Registers a class for use in creating decoration views for a collection view. This method gives the layout object a chance to register a decoration view for use in the collection view. Decoration views provide visual adornments to a section or to the entire collection view but are not otherwise tied to the data provided by the collection view’s data source.You do not need to create decoration views explicitly. After registering one, it is up to the layout object to decide when a decoration view is needed and return the corresponding layout attributes from its layoutAttributesForElements(in:) method. For layout attributes that specify a decoration view, the collection view creates (or reuses) a view and displays it automatically based on the registered information.
If you previously registered a class or nib file with the same kind string, the class you specify in the viewClass parameter replaces the old entry. You may specify nil for viewClass if you want to unregister the decoration view.

Figure 15

The background card view that shows each section background view as a card

Figure 16

Global Header , Footer and Decorative View

As shown in Figure 17 we added header and footer globally to layout instead of section, since section and footer pinToVisibleBounds is true so it will be visible to screen

UICollectionViewCompositionalLayoutConfiguration → An object that defines scroll direction, section spacing, and headers or footers for the layout.You use a layout configuration to modify a collection view layout’s default scroll direction, add extra spacing between each section of the layout, and add headers or footers to the entire layout. You can pass in this configuration when creating a UICollectionViewCompositionalLayout, or you can set the configuration property on an existing layout. If you modify the configuration on an existing layout, the system invalidates the layout so that it will be updated with the new configuration.

Figure 17

As shown in Figure 18 now pinToVisibleBounds comment out ow when you scroll global header and footer will scroll as well

Figure 18

Useful links

--

--

Ali Akhtar

Senior iOS Engineer | HungerStation | Delivery Hero