Detect when a tab bar item is pressed

SwiftUitabbarcontrollerUitabbar

Swift Problem Overview


I have a root view controller which isn’t set as the custom class for any of my view controllers on my storyboard. Instead, all of my view controllers are subclassing this class like so.

// RootViewController
class RootViewController: UIViewController, UITabBarDelegate { 

    // This is not getting executed on any of the view controllers
    func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
        print("ddd")
    }
}

// Subclassing it 
class TopStoriesViewController: RootViewController {

}

But I seem to be struggling with doing something when a tabbaritem is pressed on the view controller which is subclassing the rootviewcontroller, i.e, the message is not being printed.

Swift Solutions


Solution 1 - Swift

You don't want your view controller's base class to be a UITabBarDelegate. If you were to do that, all of your view controller subclasses would be tab bar delegates. What I think you want to do is to extend UITabBarController, something like this:

class MyTabBarController: UITabBarController, UITabBarControllerDelegate {

then, in that class, override viewDidLoad and in there set the delegate property to self:

self.delegate = self

Note: This is setting the tab bar controller delegate. The tab bar has it's own delegate (UITabBarDelegate), which the tab bar controller manages, and you are not allow to change.

So, now this class is both a UITabBarDelegate (because UITabBarController implements that protocol), and UITabBarControllerDelegate, and you can override/implement those delegate's methods as desired, such as:

// UITabBarDelegate
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
    print("Selected item")
}

// UITabBarControllerDelegate
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
    print("Selected view controller")
}

I'm guessing you're probably more interested in the latter. Check out the documentation to see what each of these delegates provide.

Last thing, in your storyboard (assuming you are using storyboards), set your tab bar controller's class to MyTabBarController in the Identity Inspector, and you're good to go.

Swift 3/4

// UITabBarDelegate
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
    print("Selected item")
}

// UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    print("Selected view controller")
}

Solution 2 - Swift

Doing it this way caused me an error

> Changing the delegate of a tab bar managed by a tab bar controller is > not allowed

This is what I did and it worked

  1. In you ViewController you inherit UITabBarControllerDelegate
  2. Set delegate in a viewDidLoad
  3. Add a function

Example:

class MyClass: UIViewController, UITabBarControllerDelegate {

   func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        let tabBarIndex = tabBarController.selectedIndex
        if tabBarIndex == 0 {
            //do your stuff
        }
   }

   override func viewDidLoad() {
        super.viewDidLoad()
        self.tabBarController?.delegate = self
   }

}

Solution 3 - Swift

Here is a version of @mbeaty's answer with a little more context. It is adapted from my fuller answer here.

import UIKit

class MyTabBarController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // tell our UITabBarController subclass to handle its own delegate methods
        self.delegate = self
    }

    // called whenever a tab button is tapped
    func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        
        if let firstVC = viewController as? FirstViewController {
            firstVC.doSomeAction()
        }
        
        if viewController is FirstViewController {
            print("First tab")
        } else if viewController is SecondViewController {
            print("Second tab")
        }
    }

    // alternate method if you need the tab bar item
    override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
        // ...
    }
}

Set this as the Custom class of your Tab View Controller in IB.

enter image description here

Alternate solution

  • Just do something in the viewDidLoad method of the tab's view controller. See this answer.

Solution 4 - Swift

Swift 5

To detect if a specific TarBarItem has been selected I use this on my custom TabBarController:

    class MainTabBarController: UITabBarController, UITabBarControllerDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.delegate = self
            
        }
        
        
   func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
        
        let selectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController)!
        if selectedIndex == 0 {
            print("first tab bar was selected") 
        } else {
            //do whatever
        }
    }
 
}

Solution 5 - Swift

I used this code, because I need to know that the same view controller is selected.

import UIKit

protocol CustomTabBarControllerDelegate {
    func onTabSelected(isTheSame: Bool)
}

class CustomTabBarController: UITabBarController, UITabBarControllerDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.delegate = self
    }
    
    func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
        (viewController as? CustomTabBarControllerDelegate)?.onTabSelected(isTheSame: selectedViewController == viewController)
        
        //Uncomment this code if you use UINavigationViewController in some of your tab view controllers
        //if let navigationViewController = viewController as? UINavigationViewController {
        //    (navigationViewController?.viewControllers.first as? CustomTabBarControllerDelegate)?.onTabSelected(isTheSame: selectedViewController == viewController)
        //}

        return true
    }
}

First tab view contoller

import UIKit

class Tab1ViewController: UIViewController, CustomTabBarControllerDelegate {
    func onTabSelected(isTheSame: Bool) { 
        print("Tab1ViewController onTabSelected")
        //do something
    }
}

Second tab view contoller

import UIKit

class Tab2ViewController: UIViewController, CustomTabBarControllerDelegate {
    func onTabSelected(isTheSame: Bool) { 
        print("Tab2ViewController onTabSelected")
        //do something
    }
}

Don't forget to set CustomTabBarController as custom class to your TabBarController in your storyboard and to implement CustomTabBarControllerDelegate protocol by all your tab view controllers.

The structure of your storyboards can be like below or it can have own navigation view controller for every or some of your tab view controllers (then you need to uncomment the code).

The structure of storyboard

Solution 6 - Swift

Swift 5 Easy way Enjoy

//MARK:- it will work in 
class TabBar: UITabBarController, UITabBarControllerDelegate {

}

Code

override func viewDidLoad() {
    super.viewDidLoad()

    self.selectedIndex = 1
    self.title = "Measure"

    self.delegate = self        
}

//MARK:-  UITabBarDelegate
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
    print("Selected item")
}

//MARK:- UITabBarControllerDelegate
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    let selectedIndex = tabBarController.viewControllers?.firstIndex(of: viewController)!
    if selectedIndex == 0 {
        self.title = "History"
    }
    else if selectedIndex == 1{
        self.title = "Measure"
    }
    else if selectedIndex == 2 {
        self.title = "Setting"
    } else {
        //do whatever
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTundsView Question on Stackoverflow
Solution 1 - SwiftmbeatyView Answer on Stackoverflow
Solution 2 - SwiftGulzView Answer on Stackoverflow
Solution 3 - SwiftSuragchView Answer on Stackoverflow
Solution 4 - SwiftMarutaView Answer on Stackoverflow
Solution 5 - SwiftZhebzhik BabichView Answer on Stackoverflow
Solution 6 - SwiftShakeel AhmedView Answer on Stackoverflow