UINavigationBar and method swizzling
April 12, 2011
You don’t have to use many iOS apps before you realize that several deviate from the standard gray/blue look and feel. Facebook, yelp, and zipcar all use different colors on the UINavigationBar to provide a unique look. When writing my own apps I wanted to do something similar. You can set the tintColor of the navigation bar or customize the UI further by using an image as the background.
Check out UIAppearance for a more modern solution. There methods described here are for iOS 4 and earlier.
I found several links discussing about method swizzling as a way to change the background color on a UINavigationBar. Not only did this sound like a really cool objective-c feature to learn about but it was also easy to implement. I used code similar to Devin and Sam’s implementations
First create a category class called UINavigationBar+CustomBackground.m or something similar. The plus sign in the middle of the file name is a convention followed by Apple.
#import "UINavigationBar+CustomBackground.h"
@implementation UINavigationBar (CustomBackground)
- (void)drawRectCustomBackground:(CGRect)rect {
if (self.barStyle == UIBarStyleDefault) {
UIImage *image = [UIImage imageNamed:@"title-bar.png"];
CGRect fillRect = CGRectMake(0.0, 0.0,
self.frame.size.width,
self.frame.size.height);
CGContextDrawImage(UIGraphicsGetCurrentContext(), fillRect, [image CGImage]);
return;
}
// we swizzled drawRect: method with this one
[self drawRectCustomBackground:rect];
}
@end
In your main.m you preform the actual swizzle and exchange the implementations of drawRectCustomBackground: and drawRect: with each other. Now whenever someone calls drawRect: on the nav bar, our code inside drawRectCustomBackground: will be executed. If our code inside drawRectCustomBackground: need to execute, we call the default method implementation by calling ourselves again. But since we swapped or swizzled method implementations we are actually calling the default dectRect: method.
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// swizzle the nav bar and exchange method implementations
Method drawRectCustomBackground =
class_getInstanceMethod([UINavigationBar class],
@selector(drawRectCustomBackground:));
Method drawRect =
class_getInstanceMethod([UINavigationBar class],
@selector(drawRect:));
method_exchangeImplementations(drawRect, drawRectCustomBackground);
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
It’s important to note that the call to [self drawRectCustomBackground:rect] on the last line of drawRectCustomBackground: doesn’t actually call back to drawRectCustomBackground: because we swapped method implementations.
Method swizzling is part of the dynamism of Objective-C and the decision to swizzle should not be made lightly. You’re using the framework in a manner not originally designed for. At the same time, experimentation can lead towards diversification.