[SC]()

iOS. Apple. Indies. Plus Things.

Basic Keyboard Navigation for Collection & Tableview.

// Written by Jordan Morgan // Nov 7th, 2022 // Read it in about 1 minutes // RE: Snips

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 ✌️

···

Spot an issue, anything to add?

Reach Out.