Customizing UINavigationBar and UIBarButtonItem in iOS

UINavigationBar tutorial

Recently I was on a project where we needed to customize the UINavigationBar and UIBarButtonItem look. I've done this in other apps prior to iOS 5 and for the most part it hasn't been the easiest thing to do. However, Apple keeps making things easier on us. Since most of the new development these days is happening for iOS 5 and higher, I am going to ignore the old techniques people used (myself included) as they were mostly hacks and just overall, really ugly. So we will be focusing on using UIAppearance, which was introduced in iOS 5 and makes life much easier.

Now, the point in this post is not to demonstrate my incredible taste in colors and graphics design, although I must admit that my color and graphics choices here are among the best I've ever seen. Actually, the color choices were intentionally ugly so you could see the stark difference in components. You can make things look better by using real styled button with a border and curved corners, the point here isn't to show you how to do that, but how to put those graphics on the UINavigationBar. So without any more time wasted...

Let's Begin

As always, I'm going to assume you've created a project before otherwise you would probably be searching for "how to create an iOS project' rather than "How to Customize the UINavigationBar".

When you create a new navigation project (Also known as Master/Detail now) you get a very bland looking navigation controller. In figure 1 we have a picture of what you see originally:

stock UINavigationController

Figure 1: The Master-Detail controller before any customization looks pretty dull. It looks like every other iOS app out there. Notice the background and how "flat" it looks against the view below it.

As you can see, that's pretty normal. If you need or want to have some branding in your app, this may require a customized bar. In this post, I will walk you through some of the options you have in updating the look and feel of this bar. There may be others I'm not aware of, but this will get you going in the right direction.

 

UINavigationBar Tint Color

So I've decided that my app doesn't need the normal blue UINavigationBar, but instead, I want a red tint to it. Surprisingly this is extremely easy to do, and can be done for the entire app in one location. I've done this in the AppDelegate didFinishLaunchingWithOptions method.

Adding this line:

[[UINavigationBar appearance] setTintColor:[UIColor redColor]];

Changes the tint color of the UINavigationBar, so now, application wide, it looks like the image in Figure 2

red tinted UINavigationBar

Figure 2: We set the tint color using the UINavigationBar UIAppearance to red, giving us a red status bar and red navigation bar

Now, as you can see, the UINavigationBar and the UIBarButtonItems all have a red tint, yet still look stock

Fully Customizing the UINavigationBar

Now, let's pretent that I've decided that I "LOVE" my header on my website (Not quite true, so we have to pretend here) and I want to use it in my new iOS app.

In iOS 4 and earlier, there was a lot involved to make this change. The only thing I ever got to work was subclassing UINavigationBar and overriding drawRect to make the changes, not exactly   an easy or elegant solution. This time, Apple came to the rescue!

UIAppearance Available

Now, the steps to change the background to an image are really pretty simple

Create your background image

Create images for your background that are 44X320 (and 88X640 for retina)and add them to your project (Hint: If you must work on iPad or in landscape as well, you could make these stretchable images, but probably not with my background. You would have to do some trickery to make that image work well on all sizes and platforms)

Now, add the background to the screen as follows gets you about 80% there:

[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"ImageName"] forBarMetrics:UIBarMetricsDefault];

Closer but the UIBarButtonItems are still the wrong

We're close, but not quite there yet. Notice how the background has changed,but the buttons are still the same ole ugly default buttons as in Figure  3

UINavigationBar with custom background

Figure 3: Our background low looks right, but we still have the wrong buttons. How will we fix that?

Changing the UIBarButtonItem appearance

Fortunately, changing the UIBarButtonItem look is just as easy as the UINavigationBar. In my example, I want the buttons to be smooth borderless and seamlessly integrated into the UINavigationBar. Setting their background image to nil will not work, n, you still get the default buttons. However, if you create a small, (mine is 10X10 and 20X20) image that is just a transparency, and add it to the UIBarButtonItem appearance as follows:

[[UIBarButtonItem appearance] setBackgroundImage:[UIImage imageNamed:@"clear"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault] ;

Now you should have a UINavigationBar that has "transparent" borderless buttons and a custom background as Figure 4:

UINavigationBar with transparent UIButtonBarItems

Figure 4: Now, that's better. Of course, if we wanted to make those buttons stand out, we could have borders around them or make them a different color.

A quick Recap of changing the UINavigationBar look

Finally, that UINavigationBar is starting to look pretty good. To recap what we've done so far, it's really quite simple. All of these changes have been managed using UIAppearance with all changes in the App Delegate

Changing the tint color was just one line:

[[UINavigationBar appearance] setTintColor:[UIColor redColor]];

You can use the contents of an image (think gradient or repeating pattern here) by setting the TintColor to [UIColor colorWithPatternImage] (which could also be a stretchable image)

To change the background image of the UINavigationBar and it's UIBarButtonItems, use  create the required images in the required sizes and add these lines:

[[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"image name"] forBarMetrics:UIBarMetricsDefault];

[[UIBarButtonItem appearance] setBackgroundImage:[UIImage imageNamed:@"clear image name"] forState:UIControlStateNormal barMetrics:

Adding a drop shadow to UINavigationBar

Now, that bar looks pretty good, well except for my horrible background and it still looks like it's flat against the table. Fortunately, adding a drop shadow is nearly just as easy. You'll see a lot of things online that say you need to create a category or subclass UINavigationBar but in reality, it's pretty easy to add a drop shadow.

NOTE: This change is one you would have to make on a per navigation controller basis. If, for example, you have a master/detail project, you would need to add it to both the UINavigationController navigationBar for both the master and the detail.

First, import the QuartzCore framework.

Adding QuartzCore to XCode

Figure 5: Adding the QuartzCore framework to XCode

Now, import <QuartzCore/QuartzCore.h> into the controller's .m file and  in the viewDidLoad method for each controller,  and add

    self.navigationController.navigationBar.layer.shadowOpacity = 0.75f;

    self.navigationController.navigationBar.layer.shadowColor = [UIColor blackColor].CGColor;

    self.navigationController.navigationBar.layer.shadowOffset = CGSizeMake(0,3.0);

Save and run, and you will now have a drop shadow as in Figure 6:

UINavigationBar with Drop shadow

Figure 6: Now we have a custom UINavigationBar with a drop shadow

 

The fully customized UINavigationBar

One thing you could do different for the drop shadow, is you could in fact, create a category and create a method within the category such as "toggleDropShadow:(BOOL)on" and then import that method into each of the UINavigationControllers, allowing you a single place to make changes and a simple way to turn it on or off per navigation controller. If I were looking at this for a system with more than one UINavigationController, I think this is exactly the approach I would take.