Tag Archive > UInavigationBar

Customizing the UINavigationController

Ricki » 16 June 2010 » In Cocoa Touch, Objective C » 1 Comment

I had to add some new tags to my blog as the content will mostly be Cocoa Touch, Objective C, iPhone stuff from now on.

I think the UINavigationController is by far the most widely used Controller-controller.

Out of the box the API’s for customizing the looks of the UINavigatioController are limited to setting a tint for the background color.

I am doing an iPhone app and I would like to have the freedom to set the title label, the color, background image and the buttons to whatever I like. I, however, do not want to get rejected by the App Store approval process or implement my own controller, so in this post I’ll deliver some Proof-of-Concept code for how to do this.

The product:

Yes you are a pretty girl..

The finished product

The caveats:

There are two different approaches involved: one is to build a category on the UINavigationBar, which is the view of the UINavigationController, to set the background to either a static color or a custom image. The other approach is to build a utility Class with one class method that alters the the look of a UIViewControllers UINavigationController. Notice that I wrote that we alter the look of the UINavigationController for each ViewController. There are caveats involved! This is just a Proof of Concept (feel free to add to the code and feel especially free to show me afterwards).

  • It does not support rotation!
  • The size of the navigationBar buttons are fixed’!
  • The background color/image for the UINavigationBar can not be changed at runtime.
  • You need to implement a tap handler method to catch the actions from the buttons on the navigationBar.

Probably other things… but it can be added and maybe it will.

The code:

First off, let’s construct a category for the UINavigationBar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  UINavigationBar+customLook.h
//  UINavigationBarOverrideDrawRectPOC
//
//  Created by Ricki Gregersen on 14/6/10.
//  Copyright 2010 www.rickigregersen.com. Use as you please:)
//

#import

@interface UINavigationBar (customLook)

- (void) drawRect:(CGRect)rect;

@end

And the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#import "UINavigationBar+customLook.h"

@implementation UINavigationBar (customLook)

- (void)drawRect:(CGRect)rect {

/* comment in this part to use a color instead of an image
    UIColor *color = [UIColor redColor];
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColor(context, CGColorGetComponents( [color CGColor]));
    CGContextFillRect(context, rect);
    self.tintColor = [UIColor darkGrayColor];// color;
*/

// comment out this part to use a color instead of an image
    UIImage *img    = [UIImage imageNamed: @"nav_bar_back.png"];
    [img drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}

@end

The drawRect method is invoked every time a UIViewController is pushed onto the screen. You could therefore make global variable that held a reference to the image or color if you needed it to change at runtime.

Now onto the the UINavigaitonModifier Class. This contains one class method that takes a UIViewController, a title string for the left and right button, and a boolean for the left and right button determining if it is a square or an “arrow”. (the usual back button is an arrow). The class also defines a protocol for receiving actions when either button is tapped.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//  ISNavigationModifier.h
//  UINavigationBarOverrideDrawRectPOC
//
//  Created by Ricki Gregersen on 14/6/10.
//  Copyright 2010 www.rickigregersen.com. Use as you please:)
//

#import

@protocol ISNavigationModifierProtocol

@optional

- (void) leftBarButtonHandler:(id) sender;
- (void) rightBarButtonHandler:(id) sender;

@end

@interface ISNavigationModifier : NSObject {

}

+ (void) modifyNavigationBarFor:(UIViewController*) target : (NSString*) leftButtonString : (BOOL) isLeftArrow : (NSString*) rightButtonString : (BOOL) isRightArrow;

@end

The implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//  ISNavigationModifier.m
//  UINavigationBarOverrideDrawRectPOC
//
//  Created by Ricki Gregersen on 14/6/10.
//  Copyright 2010 www.rickigregersen.com. Use as you please:)
//

#import "ISNavigationModifier.h"

@implementation ISNavigationModifier

+ (void) modifyNavigationBarFor:(UIViewController*) target : (NSString*) leftButtonString : (BOOL) isLeftArrow : (NSString*) rightButtonString : (BOOL) isRightArrow {

    UIButton *rightButton = nil;
    UIButton *leftButton = nil;

    if (leftButtonString) {

        leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [leftButton addTarget:target action:@selector(leftButtonHandler:) forControlEvents:UIControlEventTouchUpInside];
        [leftButton.titleLabel setFont:[UIFont fontWithName:@"Thonburi-Bold" size:12]];
        [leftButton setTitle:leftButtonString forState:UIControlStateNormal];

        if (isLeftArrow) {
            [leftButton setFrame:CGRectMake(0.0f, 0.0f, 79.0f, 37.0f)];
            [leftButton setBackgroundImage:[UIImage imageNamed:@"arrow_square_btn.png"] forState:UIControlStateNormal];
        } else {
            [leftButton setFrame:CGRectMake(0.0f, 0.0f, 79.0f, 37.0f)];
            [leftButton setBackgroundImage:[UIImage imageNamed:@"square_btn.png"] forState:UIControlStateNormal];
        }
   }

    if (rightButtonString) {

        rightButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [rightButton addTarget:target action:@selector(rightButtonHandler:) forControlEvents:UIControlEventTouchUpInside];
        [rightButton.titleLabel setFont:[UIFont fontWithName:@"Thonburi-Bold" size:12]];
        [rightButton setTitle:rightButtonString forState:UIControlStateNormal];

        if (isLeftArrow) {
            [rightButton setFrame:CGRectMake(0.0f, 0.0f, 79.0f, 37.0f)];
            [rightButton setBackgroundImage:[UIImage imageNamed:@"arrow_square_btn.png"] forState:UIControlStateNormal];
        } else {
            [rightButton setFrame:CGRectMake(0.0f, 0.0f, 79.0f, 37.0f)];
            [rightButton setBackgroundImage:[UIImage imageNamed:@"square_btn.png"] forState:UIControlStateNormal];
        }
    }

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150.0f, 20.0f)];
    [label setFont:[UIFont fontWithName:@"Thonburi-Bold" size:14]];
    [label setBackgroundColor:[UIColor clearColor]];
    [label setTextAlignment:UITextAlignmentCenter];
    [label setText:target.title];
    [target.navigationItem setTitleView:label];
    [label release];

    UIBarButtonItem *leftItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
    UIBarButtonItem *rightItem = [[UIBarButtonItem alloc] initWithCustomView:rightButton];
   
    [target.navigationItem setLeftBarButtonItem:leftItem];
    [target.navigationItem setRightBarButtonItem:rightItem];

    [leftItem release];
    [rightItem release];
}

@end

The modifier class uses the reference to the UIViewController to change the navigationBar.

Example:

To use the above classes; put the UINavigationBar+customLook.h and UINavigationBar+customLook.m files somewhere in your project. Change the images or color property to fit your custom style.
Also add the ISNavigationModifier.h and ISNavigationModifier.m.
In a viewController that is pushed onto a NavigationController; import the ISNavigationModifier.h file, implement the methods: leftButtonHandler and rightButtonHandler.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (void) viewDidLoad {

    [super viewDidLoad];
    self.title = @"I am a pretty girl";
    [ISNavigationModifier modifyNavigationBarFor:self :@"Back" :NO :@"Next" :NO];
}

- (void) leftButtonHandler:(id) sender {

    [self.navigationController popViewControllerAnimated:YES];
    NSLog(@"Left Button Tapped!");
}
- (void) rightButtonHandler:(id) sender {

    ISViewControllerThree *vcThree = [[ISViewControllerThree alloc] initWithNibName:@"ISViewControllerThree" bundle:nil];
    [self.navigationController pushViewController:vcThree animated:YES];
    [vcThree release];
    NSLog(@"Right Button Tapped!");
}

Feel free to put  rants, comments, questions and improvements in the comments below.

Continue reading...

Tags: , , ,