Programmatic UICollectionViewController

I'm making a UIKit app with no storyboard.

This is my scene delegate:

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
    var window: UIWindow?
    
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        guard let windowScene = (scene as? UIWindowScene) else { return }
        
        let window = UIWindow(windowScene: windowScene)
        window.makeKeyAndVisible()
        window.rootViewController = UINavigationController(rootViewController: ViewController())
        self.window = window
    }
}

I've noticed that if I subclass ViewController to UICollectionViewController, the app crashes with message "Thread 1: "UICollectionView must be initialized with a non-nil layout parameter"":

import UIKit

class ViewController: UICollectionViewController {

}

It looks like I necessarily need to override the initializer:

import UIKit

class ViewController: UICollectionViewController {
    
    init() {
        super.init(collectionViewLayout: .init())
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

I would indeed like to pass the final collection view layout in super.init(collectionViewLayout:), but defining the trailing actions before that isn't possible since self hasn't been initialized yet.

So this is what I'm stuck with:

import UIKit

class ViewController: UICollectionViewController {
    
    init() {
        super.init(collectionViewLayout: .init())
        
        var configuration = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
        var layout = UICollectionViewCompositionalLayout.list(using: configuration)
        
        configuration.trailingSwipeActionsConfigurationProvider = { [weak self] indexPath -> UISwipeActionsConfiguration? in
            // access a property of self
            return .init(actions: [.init(style: .destructive, title: "Hello", handler: { _,_,_ in print("Handled") })])
        }
        layout = UICollectionViewCompositionalLayout.list(using: configuration)
        collectionView.collectionViewLayout = layout
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Is this all valid?

Answered by DTS Engineer in 797665022

I've noticed that if I subclass ViewController to UICollectionViewController, the app crashes with message "Thread 1: "UICollectionView must be initialized with a non-nil layout parameter"":

My best guess is that the view controller in your storyboard wasn't configured correctly.

When you add the view controller to your storyboard, try to add a Collection View Controller, and not a View Controller. That should give you a configured collection view controller. Then, in the storyboard, you can set the controller's class to your UICollectionViewController subclass, and initialize the layout in your controller's viewDidLoad.

In the case where you need to create your UICollectionViewController subclass programmatically (and not by loading from a storyboard), calling super.init with a dummy layout and setting up the layout after that, as shown in your code snippet, looks good to me.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Have you searched for tutorials ?

Some here:

https://medium.com/@thenoobdev10/programmatic-uicollectionviewcontroller-tutorial-925f009c98a7

They explain how to create a property for layout and initialize collectionViewController with flowLayout. Update in AppDelegate

Another interesting reference: https://www.appsdeveloperblog.com/create-uicollectionview-programmatically-in-swift/

Accepted Answer

I've noticed that if I subclass ViewController to UICollectionViewController, the app crashes with message "Thread 1: "UICollectionView must be initialized with a non-nil layout parameter"":

My best guess is that the view controller in your storyboard wasn't configured correctly.

When you add the view controller to your storyboard, try to add a Collection View Controller, and not a View Controller. That should give you a configured collection view controller. Then, in the storyboard, you can set the controller's class to your UICollectionViewController subclass, and initialize the layout in your controller's viewDidLoad.

In the case where you need to create your UICollectionViewController subclass programmatically (and not by loading from a storyboard), calling super.init with a dummy layout and setting up the layout after that, as shown in your code snippet, looks good to me.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Programmatic UICollectionViewController
 
 
Q