I've overrided(placed in category, or swizzled) UINavigationBar's drawRect to show custom background. In iOS 5 it's not working. What should I do?
-
what should be the dimension of the image? – Mar 26 '12 at 13:40
7 Answers
Setting custom background for UINavigationBar to support iOS5 and iOS4 too!
http://rogchap.com/2011/06/21/custom-navigation-bar-background-and-custom-buttons/
As you know, until iOS 5 came out, we used drawRect
override in AppDelegate
to customize UINavigationBar
.
But know, iOS 5 give us some new method for styling (and old doesn’t work).
How to build app that will work on iOS 4 and iOS 5 with stylized UINavigationBar
?
You must to do both!
In AppDelegate
use this code:
@implementation UINavigationBar (UINavigationBarCategory)
- (void)drawRect:(CGRect)rect {
UIImage *img = [UIImage imageNamed:@"navbar.png"];
[img drawInRect:rect];
}
@end
and in viewDidLoad
method for iOS5 (in your view implementation):
if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar.png"] forBarMetrics:UIBarMetricsDefault];
}
If you see, here we are asking if navbar will respondToSelector to avoid crash on iOS4!

- 54,294
- 25
- 151
- 185

- 11,108
- 15
- 49
- 67
-
This is a bit different solution. In this solution, you should always set up Navigation bar for each viewController. but for iOS4 and iOS5 support. It's a bit better – tt.Kilew Nov 08 '11 at 09:42
-
I put the code in my AppDelegate. Every ViewController has the image in the NavigationBar. It seems that you have to implement it only one time and not in EVERY ViewController :-) – Manni Nov 08 '11 at 09:51
-
In case of iOS5 device you should place it in EVERY controller, that has new Navigation controller – tt.Kilew Nov 08 '11 at 10:22
-
This proposed solution is working fine except in a Tabbar application where there is more than 4 tabs. The fifth and subsequent one don't get the customization done... any ideas? – JFMartin Nov 11 '11 at 03:05
Here's a less-ugly solution that works for both iOS4 and 5:
@implementation UINavigationBar (CustomBackground)
- (UIImage *)barBackground
{
return [UIImage imageNamed:@"top-navigation-bar.png"];
}
- (void)didMoveToSuperview
{
//iOS5 only
if ([self respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)])
{
[self setBackgroundImage:[self barBackground] forBarMetrics:UIBarMetricsDefault];
}
}
//this doesn't work on iOS5 but is needed for iOS4 and earlier
- (void)drawRect:(CGRect)rect
{
//draw image
[[self barBackground] drawInRect:rect];
}
@end

- 40,865
- 11
- 112
- 103
-
Great, working and simple when coming from the category solution and too lazy to go on a subclass trick. – kheraud Feb 27 '12 at 15:14
Try to read iOS 5.0 Release Notes
In iOS 5, the UINavigationBar, UIToolbar, and UITabBar implementations have changed so that the drawRect: method is not called unless it is implemented in a subclass. Apps that have re-implemented drawRect: in a category on any of these classes will find that the drawRect: method isn't called. UIKit does link-checking to keep the method from being called in apps linked before iOS 5 but does not support this design on iOS 5 or later.

- 25,519
- 37
- 106
- 129

- 2,511
- 3
- 19
- 22
There's some possible solutions:
Quickest fix For laziest of us :
@interface MyNavigationBar : UINavigationBar
@end
@implementation MyNavigationBar
- (void)drawRect:(CGRect)rect {
}
@end
@implementation UINavigationBar (BecauseIMLazyHacks)
/*
Another Ugly hack for iOS 5.0 support
*/
+ (Class)class {
return NSClassFromString(@"MyNavigationBar");
}
-(void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, self.frame.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(0, 0,
self.frame.size.width, self.frame.size.height), barBackground.CGImage);
}
@end
Again. It works, but You shouldn't do it like this.
Another way, as suggested in WWDC'11 is to override UINavigationBar (Create MyNavigationBar) and initialize UINavigationController from xib like here :
http://www.iosdevnotes.com/2011/09/custom-uinavigationbars-techniques/
And finally, use logic flow switch for iOS5.0 and iOS5.0- Use new API where it's possible.
Categories is wrong path, Swizzling is wrong path. (They're just whispering in your ears:"Give yourself to the Dark Side. It is the only way you can save your apps.")

- 5,954
- 2
- 33
- 51
-
3drawRect code should be on MyNavigationBar implementation, not in the Category. – Matteo Alessani Oct 17 '11 at 13:20
-
What is that barBackground.CGImage by the way? It seems nowhere else in the above code. – Selvin Nov 03 '11 at 05:19
-
barBackground has UIImage class, so CGIImage just property wit CGImagRef of UIImage class. – tt.Kilew Nov 21 '11 at 12:18
-
what are the drawbacks of doing it this way (apart from ugliness?). Will Apple object? – Sam Feb 24 '12 at 12:35
-
At the current time, Apple didn't say anything about this method in App, where I forced to use this method :) But Apple so Apple. You never know when they restrict something:) – tt.Kilew Feb 25 '12 at 07:19
@implementation UINavigationBar (MyCustomNavBar)
- (void)setBackgroudImage:(UIImage*)image
{
CGSize imageSize = [image size];
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, imageSize.height);
UIImageView *backgroundImage = [[UIImageView alloc] initWithImage:image];
backgroundImage.frame = self.bounds;
[self addSubview:backgroundImage];
[backgroundImage release];
}
@end
The above swizzling will allow you to set any custom background image for the UINavigationBar(iOS5 & iOS4).

- 12,333
- 17
- 59
- 80
Follow this link to make your code compatible with iOS4, 5 and 6.
You just have to make in Photoshop or other software a rectangular with the size of 320x44 or 640x88 (for retina display) and import it to your project
In AppDelegate use this code (in the header between #import and @implementation AppDelegate):
@implementation UINavigationBar (CustomImage)
- (void)drawRect:(CGRect)rect {
UIImage *image = [UIImage imageNamed:@"top_bar.png"];
[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
@end
In viewDidLoad use this code for iOS5 and iOS6:
#if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
if ([self.navigationController.navigationBar respondsToSelector:@selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"top_bar.png"] forBarMetrics:UIBarMetricsDefault];
}
#endif

- 477
- 1
- 7
- 15
After iOS 5 - (void)drawRect:(CGRect)rect
is not called while we create category for UINavigationBar
but you can call -(void)awakeFromNib
and add all the code that you want to add.

- 5,318
- 10
- 50
- 84