Search code examples
iosswiftuitabbarcontrolleruitabbaritem

UITabBar selected tab doesn't change tint color on launch


I have subclassed UITabBarController to allow for some customization specific to my app. It is the root view controller of my UIWindow and displays itself correctly on launch, and even shows the correct tab's view hierarchy as well.

The problem is with the selected tabbar item's tint color. Inside viewDidLoad of the custom tab bar controller subclass, I have set both the unselected and selected tint colors for the tab bar. See below:

override func viewDidLoad() {
    super.viewDidLoad()

    tabBar.tintColor = .tabBarItemActiveTint
    tabBar.unselectedItemTintColor = .tabBarItemInactiveTint
    tabBar.barTintColor = .tabBarBg

    let dashboardVC = DashboardViewController.build()
    let settingsVC = SettingsTableViewController.build()
    let settingsNavC = UINavigationController(rootViewController: settingsVC)
    settingsNavC.navigationBar.barStyle = .black

    viewControllers = [dashboardVC, settingsNavC]
    selectedViewController = dashboardVC

    // Accessing the view property of each tab's root view controller forces
    // the system to run "viewDidLoad" which will configure the tab icon and
    // title in the tab bar.
    let _ = dashboardVC.view
    let _ = settingsVC.view
}

As you can see, the controller has its child view hierarchies set, and the views are loaded at the bottom so their respective viewDidLoad methods run where I have code that sets the tabBarItem. Here's an example from the dashboard view controller:

tabBarItem = UITabBarItem(title: "Dashboard", image: UIImage(named: Theme.dashboardTabBarIcon), tag: 0)

Everything about this works except for the selected icon and title. When the app launches I can see the tab bar, the first view hierarchy (the dashboard) is visible onscreen and the tabs all function properly. But the dashboard's icon and title are in an unselected state. I have to actually tap the tab bar icon to get the state to change such that it is selected.

Once you tap one of the tabs, the selected state works as normal. The issue is only on the first presentation of the tab bar.

Here is an image showing the initial state of the tab bar on launch. Notice the dashboard icon is not selected, even though it is the presented view controller.

broken tab bar icon, nothing selected

UPDATE

Skaal's answer below solved the problem for me.

For future reference: the key difference between the code presented here in my question and his sample in the answer is that the tabBarItem is set in viewDidLoad of his custom TabBarController class. By contrast, that code was placed within the viewDidLoad method of each constituent view controller class in my project. There must be a timing issue of when things are called that causes the tint color to not be set in one scenario and work properly in the other.

Key Takeaway: If you set up a tab bar controller programmatically, be sure to set your tabBarItem properties early on to ensure tint colors work properly.


Solution

  • You can use :

    selectedIndex = 0 // the index of your dashboardVC
    

    instead of selectedViewController

    EDIT - Here is a working sample of UITabBarController:

    class TabBarController: UITabBarController {
    
        private lazy var firstController: UIViewController = {
            let controller = UIViewController()
            controller.title = "First"
            controller.view.backgroundColor = .lightGray
            return controller
        }()
    
        private lazy var secondController: UIViewController = {
            let controller = UIViewController()
            controller.title = "Second"
            controller.view.backgroundColor = .darkGray
            return controller
        }()
    
        private var controllers: [UIViewController] {
            return [firstController, secondController]
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            tabBar.tintColor = .magenta
            tabBar.unselectedItemTintColor = .white
            tabBar.barTintColor = .black
    
            firstController.tabBarItem = UITabBarItem(title: "First", image: UIImage(), tag: 0) // replace with your image
            secondController.tabBarItem = UITabBarItem(title: "Second", image: UIImage(), tag: 1) // replace with your image
    
            viewControllers = controllers
            selectedViewController = firstController
        }
    }