Categories
iOS Swift UIKit

Flow layout with self-sizing items and fixed spacing in UIKit

One of the really common layouts I have needed to implement with collection view is a simple flow layout but with fixed spacings. Apple provides us UICollectionViewFlowLayout, but the sad part is that it has dynamic spacing between items. Everything is there but not quite. Before UICollectionViewCompositionalLayout, one needs to create a subclass of the flow layout and then fixing spacings manually, which is pretty cumbersome to do. Therefore, let’s instead see what it takes to implement a simple self-sizing flow layout with fixed spacings when using UICollectionViewCompositionalLayout. The end goal is visible below, where we have a single section with 7 items.

Flow layout with fixed spacings.

UICollectionViewCompositionalLayout was created to be a flexible layout which allows building all sorts of layouts quickly. Data in that layout is divided into sections, where each section can have one or more groups of items. Grouping allows creating more complex layouts, where each group describes how items in the group are laid out in relation to each other. But in our case we have something really simple in mind, which is having self-sizing items which we can configure with NSCollectionLayoutSize and passing estimated dimensions. Then the next step is creating NSCollectionLayoutItem with that layout size and with some space around the item. The edge spacing with fixed edges gives us the wanted fixed spacing between items. After that, we’ll create NSCollectionLayoutGroup with horizontal layout direction and with a layout size which takes max width, but height is fitted based on item sizes. Creating layouts like this is so much better compared to subclassing UICollectionViewLayout and then calculating frames one by one. Down below is the configured layout object, which has fixed spacing and items are self-sizing.

extension UICollectionViewLayout {
static func fixedSpacedFlowLayout() -> UICollectionViewLayout {
let itemSize = NSCollectionLayoutSize(
widthDimension: .estimated(50),
heightDimension: .estimated(50)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.edgeSpacing = NSCollectionLayoutEdgeSpacing(
leading: .fixed(8),
top: .fixed(4),
trailing: .fixed(8),
bottom: .fixed(4)
)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(100)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
return UICollectionViewCompositionalLayout(section: section)
}
}

Example project can be found here: GitHub

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.

Leave a comment