Customizing the UINavigationController
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:
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.
