26

I would like to know how do we detect when the selected TabBar Item or Index is changed when the changes is done programmatically?

self.tabBarController.selectedIndex = 1;

This two delegate function only detect changes when the tabBar Item was selected by user. It does not fire when the changes to the selectedIndex was done programmatically.

func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
    println("tabBarController didSelectViewController")
}

override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
    println("tabBar didSelectItem")
}
JayVDiyk
  • 4,277
  • 22
  • 70
  • 135

8 Answers8

29

Along all answers here, if you subclassed UITabBarController already, here's a simple solution for all tab bar changes (User initiated and programmatic ):

// Override selectedViewController for User initiated changes
override var selectedViewController: UIViewController? {
    didSet {
        tabChangedTo(selectedIndex: selectedIndex)
    }
}
// Override selectedIndex for Programmatic changes    
override var selectedIndex: Int {
    didSet {
        tabChangedTo(selectedIndex: selectedIndex)
    }
}

// handle new selection
 func tabChangedTo(selectedIndex: Int) {}
Meseery
  • 1,845
  • 2
  • 22
  • 19
  • 4
    Man! You saved my job! Thank you so much! It has solved my biggest problems of showing initial vc for every tab :) – iHarshil May 14 '19 at 14:08
  • Thank god for this answer, was actually starting to send me a little crazy – Chris Jul 14 '22 at 22:21
22

Previous answers are sufficient to "detect" the changes, however it does not detect which index is being pressed.

func selectItemWithIndex(value: Int) {
    self.tabBarControllertabBarController.selectedIndex = value;
    self.tabBar(self.tabBar, didSelectItem: (self.tabBar.items as! [UITabBarItem])[value]);
}

self.selectedIndex will not return the selected index right away. To check which item is being pressed, we would need to compare the item with the tabBarItems in our UITabBarController

override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
    if item == (self.tabBar.items as! [UITabBarItem])[0]{ 
       //Do something if index is 0
    }
    else if item == (self.tabBar.items as! [UITabBarItem])[1]{
       //Do something if index is 1
    }
}
Eddy Liu
  • 1,463
  • 9
  • 6
  • 2
    I'm not sure that calling directly a delegate method is a good practice because its purpose is to be fired automatically. – XLE_22 Jan 31 '19 at 09:16
5

In swift, you can do it by overriding selectedIndex property of UITabBarController.

First subclass UITabBarController and add all the following code.

//Overriding this to get callback whenever its value is changes
   override var selectedIndex: Int {
      didSet {
         handleTabSelection(selectedIndex: selectedIndex)
      }
   }

Also add this delegate method of UITabBarControllerDelegate

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    //Tab tapped
    guard let viewControllers = tabBarController.viewControllers else { return }
    let tappedIndex = viewControllers.index(of: viewController)! 
    //Tab tapped at tappedIndex
    handleTabSelection(selectedIndex: tappedIndex)
}

Finally, we call this method from both places so that all the cases are handled.

private func handleTabSelection(selectedIndex: Int) {
    //Do something on tab selection at selectedIndex
}
Vineet Ashtekar
  • 1,952
  • 21
  • 16
3

Swift 3

Here's a somewhat safer Swift-3 version, of what's been proposed above:

func selectItem(withIndex index: Int) {

    if  let controller = tabBarController,
        let tabBar = tabBarController?.tabBar,
        let items = tabBarController?.tabBar.items
    {
        guard index < items.count else { return }

        controller.selectedIndex = index
        controller.tabBar(tabBar, didSelect: items[index])
    }
}

UITabBarController:

override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
    if let items = tabBar.items {
        items.enumerated().forEach { if $1 == item { print("your index is: \($0)") } }
    }
}

Usage:

selectItem(withIndex: 1)
ziligy
  • 1,447
  • 16
  • 10
1

that can't be done but some kind of hacking it can be done and i am sure that's gonna be a temporary solution for this problem. override below method for tabbarcontrollerdelegate do your stuff inside this is called when tab is switched in either way programatically or tapping on tab bar item.

func tabBarController(_ tabBarController: UITabBarController, animationControllerForTransitionFrom fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
{
    toVC.tabBarItem.setTitleTextAttributes([NSFontAttributeName: UIFont.fontFuturaMedium11, NSForegroundColorAttributeName: UIColor.colorAppThemeColor], for: .normal)
    fromVC.tabBarItem.setTitleTextAttributes([NSFontAttributeName: UIFont.fontFuturaBTBook11, NSForegroundColorAttributeName: UIColor.colorStudentTabText], for: .normal)
    return nil
}
Amit Saxena
  • 596
  • 7
  • 18
0

I've found a lovely solution to this. Thanks to Ckouta for the idea.

I simply create a function to change the index and fire the delegate protocol of UITabBar's didSelectItem

func selectItemWithIndex(value: Int) {
    self.tabBarControllertabBarController.selectedIndex = value;
    self.tabBar(self.tabBar, didSelectItem: (self.tabBar.items as! [UITabBarItem])[value]);
}

Usage

    selectItemWithIndex(1);
JayVDiyk
  • 4,277
  • 22
  • 70
  • 135
0

I only cared about the selectedIndex in one of the VCs in the tab bar controller stack. So I KVO:ed segmentedIndex on the tabbarcontroller instance from that VC. Works well, no real problems.

Jonny
  • 15,955
  • 18
  • 111
  • 232
-1

Swift 4:- You have to use these lines of code for detecting UITabBar selected item index.

func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {

    if item == (tabBar.items)![0] {
        // do something with 1st tab
    }
    else if item == (tabBar.items)![1] {
        // do something with 2nd tab
    }
}
Deepak Verma
  • 373
  • 7
  • 19
V.Kumar
  • 139
  • 2
  • 5