I have a very similar problem to this question and this question and I think they might be related. The difference is that the delegate is not called when a generic view model is used.
In the first example below everything works as expected. Initialize a ViewController
, which is a subclass of SimpleListViewController
. When the cell is selected it prints "Here" because the superclass (SimpleListViewController
) conforms to UICollectionViewDelegate
.
class SimpleListVCModel {
var dataSource: UICollectionViewDiffableDataSource<Section, String>! = nil
var items = Array(0..<10).map{"This is item \($0)"}
enum Section: String {
case main
}
func configureDataSource(using collectionView: UICollectionView) {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> { (cell, indexPath, item) in
var content = cell.defaultContentConfiguration()
content.text = "\(item)"
cell.contentConfiguration = content
}
dataSource = UICollectionViewDiffableDataSource<Section, String>(collectionView: collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, identifier: String) -> UICollectionViewCell? in
return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: identifier)
}
}
func applySnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
snapshot.appendSections([.main])
snapshot.appendItems(items)
dataSource.apply(snapshot, animatingDifferences: false)
}
}
class ViewController: SimpleListViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "List"
view.backgroundColor = .red
configureHierarchy()
model.configureDataSource(using: collectionView)
model.applySnapshot()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Here")
}
}
class SimpleListViewController: UIViewController, UICollectionViewDelegate {
var collectionView: UICollectionView! = nil
let model = SimpleListVCModel()
func configureHierarchy() {
let layout = UICollectionViewCompositionalLayout.list(using: .init(appearance: .insetGrouped))
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.delegate = self
view.addSubview(collectionView)
}
}
Now If I introduce generics, the collectionView delegate function is never called.
First create a subclass of SimpleListVCModel
specifically for the ViewController
.
class ViewControllerViewModel: SimpleListVCModel {
func foo(){
print("Foo")
}
}
Then slightly modify SimpleListViewController
to use a generic SimpleListVCModel
.
class SimpleListViewController<M: SimpleListVCModel>: UIViewController, UICollectionViewDelegate {
let model: M
var collectionView: UICollectionView! = nil
//MARK: - Initializer
init(model: M) {
self.model = model
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Views
func configureHierarchy() {
let layout = UICollectionViewCompositionalLayout.list(using: .init(appearance: .insetGrouped))
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.delegate = self
view.addSubview(collectionView)
}
}
Finally modify ViewController
to use a generic version of the SimpleListVCModel
.
class ViewController: SimpleListViewController<ViewControllerViewModel> {
override init(model: ViewControllerViewModel = ViewControllerViewModel()) {
super.init(model: model)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "List"
view.backgroundColor = .red
configureHierarchy()
model.configureDataSource(using: collectionView)
model.applySnapshot()
model.foo()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Here")
}
}