Search code examples
iosswiftuinavigationcontroller

How to navigate from an embedded collection view’s cell to another view controller without using Storyboard or IB?


About the app (UICollectionView within UICollectionView):

  • TabBarController is the app window’s root view controller
  • TabBarController contains 3 Navigation Controllers.
  • 1st Navigation Controller has HomeViewController as root view controller
  • HomeViewController contains CategoryCollectionView
  • Inside each cell of CategoryCollectionView is present one ItemCollectionView
  • Each ItemCollectionView contains many ItemCell

Objective: When user clicks on an ItemCell, the app should navigate to a different view controller, the ItemViewController. I’m developing this app completely using code. So I want to do this without Storyboard segues or IB. I haven’t been able to figure it out so far. If you could point me in the right direction, it would be great. Thanks.

I tried the following, but they didn’t work:

First method: Accessing the window’s root view controller from within the CategoryCollectionView’s cell

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let currentNavCon = self.window?.rootViewController!.navigationController
    currentNavCon?.pushViewController(ItemViewController(), animated: true)
}

Second method: I defined a function itemCellClicked in HomeViewController, and called it from within didSelectItemAt of CategoryCollectionView’s cell.

func itemCellClicked(_ sender: CatalogViewCategoryCell, _ position: Int) {
    let itemViewController = ItemViewController()
    navigationController?.pushViewController(itemViewController, animated: true)
}

And, inside the cell:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    HomeViewController().itemCellClicked(self, indexPath.item)
}

Solution

  • Pushing view controller from cell is never a good idea. You need to propagate your click event from ItemCell to all the way to HomeViewControlller.
    In your CategoryCollectionView define a public property of type closure.

    var onDidSelectItem: ((IndexPath) -> ())?
    

    Now in CategoryCollectionView's didSelectItem, call the closure like this.

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            self.onDidSelectItem?(indexPath)
    }
    

    In your HomeViewControlller's cellForRow, receive the callback.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        categoryCollectionView.onDidSelectItem = {(indexPath) in
           //item at indexPath clicked, do whatever you want to do with it.
        }
    }