Select a specific viewController in UITabBarController without knowing its index

Viewed 1494

I have a UItabBarController (called tabBarController) composed of a number of options. I also have a UITableView, whose first row is an option that should make the user navigate to a specific viewController.

My didSelectRowAt delegate method looks something like this:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {
        /* BrowseViewController is currently the second item in the 
           tabBarController, so I just select its index to navigate to it */
        tabBarController?.selectedIndex = 2
    }
}

Now, this works for my current situation because I know that the second item in tabBarController is the UIViewController that I am looking for, but I want to future-proof my app so that if the order of viewControllers in tabBarController is changed in the future, the tableView does not break.

In other words, I am wondering if there is a way to first extract the index of the viewController I am looking for from tabBarController, and then use that index to navigate to it, something like so:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {

        let browseViewControllerIndex = Int()
        /* iterate through tabBarController's VC's and if the type of the VC 
       is BrowseViewController, find its index and store it
       in browseViewController */
    }
 }
2 Answers

You can try something like:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {

        // safely get the different viewcontrollers of your tab bar
        // (viewcontrollers is an optional value)
        guard let tabs = tabBarController.viewcontrollers else { return }

        // index(of:) gets you the index of the specified class type.
        // Also an optional value
        guard let index = tabs.index(of: BrowseViewController()) else { return }
        tabBarController?.selectedIndex = index
    }
}

I was able to figure this out on my own. The code below is what works best for my goals. It is reasonably concise and elegant (in my opinion):

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    if tableView.cellForRowAt(at: indexPath)?.textLabel?.text == "Navigate to BrowseViewController" {

        if let browseIndex = (tabBarController?.viewControllers?.filter { $0 is UINavigationController} as? [UINavigationController])?.firstIndex(where: { $0.viewControllers.first is BrowseViewController }) {
            tabBarController?.selectedIndex = browseIndex
        }

    }

}

Note that BrowseViewController is the first viewController of UINavigationController. Of course, users looking at this answer should modify their code to suit their architecture.

Related