10

I have a TabBar. I am trying to style it so that the titles on the TabBarItems have different fonts for the normal state and the selected state. For normal state I want Helvetica Neue Light and for the selected state I want Helvetica Neue Medium. No matter what I do, I can't seem to get the fonts to be different for these two states. The color changing works fine. Here is what I currently have:

  // Set the tab bar title appearance for normal state.
  [[UITabBarItem appearance] setTitleTextAttributes:@{
     NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light"
                                          size:16],
     NSForegroundColorAttributeName: [CMK8Colors grayColor]
   }
                                           forState:UIControlStateNormal];

  // Set the tab bar title appearance for selected state.
  [[UITabBarItem appearance] setTitleTextAttributes:@{
     NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Medium"
                                          size:16],
     NSForegroundColorAttributeName: [CMK8Colors blueColor]
   }
                                           forState:UIControlStateSelected];

Please help.

clocksmith
  • 6,226
  • 3
  • 32
  • 45

5 Answers5

12

Good news and bad news.

Bad news, It's a little harder than just using the appearance proxy.

Good news It's not that much harder!

Header

#import <UIKit/UIKit.h>

@interface MYTabbyViewController : UITabBarController

@end

Implementation

#import "MYTabbyViewController.h"

@implementation MYTabbyViewController

-(void)setSelectedViewController:(UIViewController *)selectedViewController
{
    [super setSelectedViewController:selectedViewController];

    for (UIViewController *viewController in self.viewControllers) {
        if (viewController == selectedViewController) {
            [viewController.tabBarItem setTitleTextAttributes:@{
                                                    NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Medium" size:16],
                                                    NSForegroundColorAttributeName: [UIColor blueColor]
                                                    }
                                         forState:UIControlStateNormal];
        } else {
            [viewController.tabBarItem setTitleTextAttributes:@{
                                                    NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue-Light" size:16],
                                                    NSForegroundColorAttributeName: [UIColor grayColor]
                                                    }
                                         forState:UIControlStateNormal];
        }
    }
}

The last part you will need is to use this subclass instead of the out-of-the-box UITabBarController. If you are using Storyboards, simply select the TabBarController, go to the Identity Inspector (third subtab in the right panel) and change UITabBarController to MYTabBarController or what have you.

But yeah! Bottom line, just update the ViewController tabBarItem!

Acey
  • 8,048
  • 4
  • 30
  • 46
  • "subclass UITabBarController and override setCurrentViewController and update the tabBarItem of the soon-to-be selected ViewController" sounds promising. I am new to iOS, a code example will earn this answer as the selected answer =) – clocksmith Aug 11 '14 at 02:06
  • This works, but only after the first select, when the tab bar appears. As it doesn't trigger selectedViewController it keeps the normal state font. – Sonia Casas Feb 20 '19 at 08:17
  • @SoniaCasas Are you saying the initially selected tabBarItem doesn’t have the correct appearance? – Acey Feb 20 '19 at 17:35
  • @Acey exactly, because selectedViewController is not being set yet. I try in ios 10, 11 and 12, same result. – Sonia Casas Feb 22 '19 at 07:38
8

@Acey's Answer is almost perfect, but only change the font after your first selection of a tabBarItem, so when the tabBar is created, it will be created with no different font for the selected tabBarItem in that moment. To achieve that i also add a didSet in selectedIndex variable.

Here's the Complete code (Swift 4.2)

final class TabBarController: UITabBarController {

override var selectedIndex: Int {
    didSet {
        guard let selectedViewController = viewControllers?[selectedIndex] else {
            return
        }
        selectedViewController.tabBarItem.setTitleTextAttributes([.font: UIFont.systemFont(ofSize: 11, weight: UIFontWeightHeavy)], for: .normal)
    }
}

override var selectedViewController: UIViewController? {
    didSet {

        guard let viewControllers = viewControllers else {
            return
        }

        for viewController in viewControllers {

            if viewController == selectedViewController {

                let selected: [NSAttributedString.Key: AnyObject] =
                    [.font: UIFont.systemFont(ofSize: 11, weight: UIFontWeightHeavy)]

                viewController.tabBarItem.setTitleTextAttributes(selected, for: .normal)

            } else {

                let normal: [NSAttributedString.Key: AnyObject] =
                    [.font: UIFont.systemFont(ofSize: 11)]

                viewController.tabBarItem.setTitleTextAttributes(normal, for: .normal)

            }
        }
    }
}

}

Sonia Casas
  • 801
  • 9
  • 18
6

@Acey´s answer in Swift 3:

    override var selectedViewController: UIViewController? {
    didSet {

        guard let viewControllers = viewControllers else {
            return
        }

        for viewController in viewControllers {

            if viewController == selectedViewController {

                let selected: [String: AnyObject] =
                    [NSFontAttributeName: UIFont.systemFont(ofSize: 11, weight: UIFontWeightHeavy),
                     NSForegroundColorAttributeName: UIColor.red]

                viewController.tabBarItem.setTitleTextAttributes(selected, for: .normal)

            } else {

                let normal: [String: AnyObject] =
                    [NSFontAttributeName: UIFont.systemFont(ofSize: 11),
                     NSForegroundColorAttributeName: UIColor.blue]

                viewController.tabBarItem.setTitleTextAttributes(normal, for: .normal)

            }
        }
    }
}
stoffen
  • 570
  • 6
  • 14
1

setSelected and setHighlighted does not work for UIBarButtonItems.

Use UIBarButtonItem's

- (void)setBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics 

Here is the documentation - https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIBarButtonItem_Class/Reference/Reference.html

Alternatively, use UIButton.

UIButton *myButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
[myButton setTitle:@"Info" forState:UIControlStateNormal];
[myButton setFont:[UIFont fontWithName:<font name> size:<font size>]];

[myButton setTitleColor:<color>;
myButton =  <frame>;
[myButton addTarget:self.webView action:@selector(<action>:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem * barButtonItem = [[UIBarButtonItem alloc]initWithCustomView:myButton];
[[UIBarButtonItem appearance] setTintColor:<color>;
[self.toolbar setItems:[NSArray arrayWithObjects: spaceItem, barButtonItem, nil]];
hackerinheels
  • 1,141
  • 1
  • 6
  • 14
  • 1
    I am not trying to change the background image. I am trying to change the font. In the documentation you linked, it does not mention font. – clocksmith Aug 11 '14 at 01:50
  • right but this is one way of giving the selected effect. You could also add a Button with a custom view to the toolbar. Then you can change the font easily. Updating the answer with that option as well. – hackerinheels Aug 11 '14 at 01:56
  • actually i am sorry i misread your post. I haven't tried this solution with tab bar. Take a look at this post (http://www.appcoda.com/ios-programming-how-to-customize-tab-bar-background-appearance/) - if this doesn't work pls let me know.. i will remove my answer and help you with the issue – hackerinheels Aug 11 '14 at 02:08
0

Swift 5+

Define your normal and selected attributes:

private let attributesNormal: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 12, weight: .regular),
    .foregroundColor: UIColor.black.withAlphaComponent(0.9)]

private let attributesSelected: [NSAttributedString.Key: Any] = [
    .font: UIFont.systemFont(ofSize: 12, weight: .semibold),
    .foregroundColor: UIColor.black]

Set any color for unselectedItemTintColor. Without this the color defined in attributesNormal won't work. Don't ask me why

tabBar.unselectedItemTintColor = .black

Then inside your UITabBarController override selectedIndex and selectedViewController:

override var selectedIndex: Int {
    didSet {
        guard let vc = viewControllers[selectedIndex] else { return }
        vc.tabBarItem.setTitleTextAttributes(attributesNormal, for: .normal)
    }
}

override var selectedViewController: UIViewController? {
    didSet {
        viewControllers?.forEach {
            let attributes = $0 == selectedViewController ? attributesSelected : attributesNormal
            $0.tabBarItem.setTitleTextAttributes(attributes, for: .normal)
        }
    }
}
budiDino
  • 13,044
  • 8
  • 95
  • 91