Basic Keyboard Navigation for Collection & Tableview.
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
Use the focus system to support basic keyboard navigation in collection and table views.
import UIKit
class KBNavViewController: UIViewController {
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
collectionView.delegate = self
// 1
collectionView.allowsFocus = true
// 2
collectionView.selectionFollowsFocus = true
return collectionView
}()
lazy var datasource: UICollectionViewDiffableDataSource<Int, VideoGame> = {
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
cell.accessories = []
}
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)
var snap = datasource.snapshot()
snap.appendSections([0])
snap.appendItems(VideoGame.data)
datasource.apply(snap)
}
}
extension KBNavViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
}
// 3
func collectionView(_ collectionView: UICollectionView, performPrimaryActionForItemAt indexPath: IndexPath) {
let vc = UIViewController()
vc.view.backgroundColor = .purple
self.navigationController?.pushViewController(vc, animated: true)
}
}
With that in place, you can now use the arrow keys to select rows, and then perform their primary action:
The Breakdown
Step 1
The first step is to opt into the focus system by setting allowsFocus
to true. This allows the collection or table view cells to become focused.
Step 2
Next, set selectionFollowsFocus
to true. This means that when a cell becomes selected, it is also focused.
Step 3
Finally, put the logic that is the primary action of the cell when tapped or clicked into the delegate method func collectionView(_ collectionView: UICollectionView, performPrimaryActionForItemAt indexPath: IndexPath)
. Of note, this code was traditionally in didSelectItemAtIndexPath
- but remember, that will now fire when the cell is focused. So, this new method solves that problem, and it’s only called when a user taps or clicks on the cell.
Until next time ✌️