Reputation: 263
I have a UICollectionView set up in a separate swift file that is being shown on HomeViewController. I want to push a DetailViewController when a collection view cell is selected. I want to do this programmatically, without storyboards. I have always pushed ViewControllers with "navigationController.pushViewController(vc, animated: true)" but I cannot access navigationController from within the UICollectionView's "didSelectItemAt" function. If it matters, I do have a custom UICollectionViewCell set up and being used for the cells.
How can I make this work? The collectionView is displaying properly, and when I select a cell, I know that didSelectItemAt is being ran as my print statement "Did select item at..." shows in the console.
After hours of googling I can't figure it out. I'm still learning the M-V-C structure so I may have a fundamental flaw in how everything is setup. I've also seen I might need to create a custom delegate to handle this, but I feel like I'm missing something simpler since the didSelectItemAt function is right there.
Here's the pertinent code from my UICollectionView swift file:
class HomeDataController: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Did select item at...")
// this is where I am trying to push the DetailViewController
}
}
Here is my HomeViewController:
class HomeViewController: UIViewController {
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 30
layout.sectionInset.top = 20
layout.sectionInset.bottom = 20
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = UIColor(named: "background")
cv.register(HomeCustomCell.self, forCellWithReuseIdentifier: "homeCell")
return cv
}()
let dataController: HomeDataController = {
let dataController = HomeDataController()
return dataController
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self.dataController
collectionView.delegate = self.dataController
view.addSubview(collectionView)
}
}
Upvotes: 2
Views: 755
Reputation: 3727
class HomeViewController: UIViewController {
let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .vertical
layout.minimumLineSpacing = 30
layout.sectionInset.top = 20
layout.sectionInset.bottom = 20
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.backgroundColor = UIColor(named: "background")
cv.register(HomeCustomCell.self, forCellWithReuseIdentifier: "homeCell")
return cv
}()
lazy var dataController: HomeDataController = {
let dataController = HomeDataController(controller: self)
dataController.didSelectionCompletion = { indexPath in
print("Called...")
}
dataController.delegate = self
return dataController
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dataSource = self.dataController
collectionView.delegate = self.dataController
view.addSubview(collectionView)
}
}
//MARK: - HomeDataControllerDelegate
extension HomeViewController : HomeDataControllerDelegate {
func homeDataController(_ controller: HomeDataController, didSelectItemAt: IndexPath) {
print("Called..")
}
}
HomeDataController.swift
protocol HomeDataControllerDelegate {
func homeDataController(_ controller : HomeDataController, didSelectItemAt : IndexPath)
}
class HomeDataController: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
private var controller : UIViewController? = nil
var delegate : HomeDataControllerDelegate? = nil
var didSelectionCompletion : ((IndexPath) -> (Void))? = nil
override init() {
super.init()
}
init(controller : UIViewController) {
super.init()
self.controller = controller
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("Did select item at...")
// this is where I am trying to push the DetailViewController
/* Method1 - using property*/
let detialController = UIViewController()
controller?.navigationController?.pushViewController(detialController, animated: true)
/* Method2 - delegate */
delegate?.homeDataController(self, didSelectItemAt: indexPath)
/* Method3 - Block */
self.didSelectionCompletion?(indexPath)
}
}
Using method 1 (using property) there is limitations of that HomeDataController
have always push only one detail controller. It's become dependent class. There also option pass destination controller into init
method of HomeDataController
again it's become more complex. But if you have fix detailViewController then you can use this method. But it's not scalable code for any specific change request.
Blocks and delegates are more standard and scalable options. Using this methods we have full control over UIController and any action.
Hope it's helps to you.
Upvotes: 1