UICollectionView with UIContentConfiguration for Cells, Compositional List Layout and Diffable Datasource.
This post is brought to you by Emerge Tools, the best way to build on mobile.
let concatenatedThoughts = """
Welcome to Snips! Here, you'll find a short, fully code complete sample with two parts. The first is the entire code sample which you can copy and paste right into Xcode. The second is a step by step explanation. Enjoy!
"""
The Scenario
Create a UICollectionView
with all of the modern APIs:
- Content configurations to display data.
- Compositional layouts to create a list layout.
- Diffable datasource.
- And modern cell registration methods, forgoing classic APIs such as identifiers and
UICollectionViewDatasource
.
import UIKit
struct VideoGame: Hashable {
let id = UUID()
let name: String
}
extension VideoGame {
static var data = [VideoGame(name: "Mass Effect"),
VideoGame(name: "Mass Effect 2"),
VideoGame(name: "Mass Effect 3"),
VideoGame(name: "ME: Andromeda"),
VideoGame(name: "ME: Remaster")]
}
class ContentConfigViewController: UIViewController {
// 1
private lazy var collectionView: UICollectionView = {
var listConfiguration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
let layout = UICollectionViewCompositionalLayout.list(using: listConfiguration)
let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
collectionView.backgroundColor = .systemGroupedBackground
return collectionView
}()
lazy var datasource: UICollectionViewDiffableDataSource<Int, VideoGame> = {
// 2
let videoGameCellConfig = UICollectionView.CellRegistration<UICollectionViewListCell, VideoGame> { cell, indexPath, model in
var contentConfiguration = cell.defaultContentConfiguration()
contentConfiguration.text = model.name
contentConfiguration.textProperties.font = .preferredFont(forTextStyle: .headline)
cell.contentConfiguration = contentConfiguration
}
// 3
let datasource = UICollectionViewDiffableDataSource<Int, VideoGame>(collectionView: collectionView) { collectionView, indexPath, model in
let configType = videoGameCellConfig
return collectionView.dequeueConfiguredReusableCell(using: configType,
for: indexPath,
item: model)
}
return datasource
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
// 4
var snap = datasource.snapshot()
snap.appendSections([0])
snap.appendItems(VideoGame.data)
datasource.apply(snap)
}
}
Now, we’ve got collection view prepared with all of the modern APIs:
The Breakdown
Step 1
First, we create the collection view by giving it a layout. This process is two-fold:
- Create the list configuration via
UICollectionLayoutListConfiguration
. - Pass it to a compositional layout object,
UICollectionViewCompositionalLayout.list(using: listConfiguration)
.
Then, we can create the collection view with a list layout, UICollectionView(frame: view.bounds, collectionViewLayout: layout)
.
Step 2
Using modern cell registration, we pass a content configuration object and assign it to a cell. This is where developers typically would use something like func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
to set data from a model to the cell. Here, we grab the default configuration and set a string and adjust the font size.
Step 3
Now, our job is to return the cell registration we just created to the diffable datasource:
return collectionView.dequeueConfiguredReusableCell(using: configType,
for: indexPath,
item: model)
At this point, we know what to show for the data we have, and we know how to vend that data.
Step 4
Finally, we create a diffable datasource snapshot and add some data to it and we’re done! We’ve now used all of the modern APIs for UICollectionView
.
Until next time ✌️