UINavigationBar's drawRect is not called in iOS 5.0
Solution 1
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.")
Solution 2
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!
Solution 3
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
Solution 4
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.
Solution 5
@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).
tt.Kilew
iOS Programmer with lot of experience My own projects Dynamic Code Injection on iOS https://github.com/dyci/dyci-main Dependency visualizer for objective-c https://github.com/PaulTaykalo/objc-dependency-visualizer
Updated on July 11, 2022Comments
-
tt.Kilew almost 2 years
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?
-
Matteo Alessani over 12 yearsdrawRect code should be on MyNavigationBar implementation, not in the Category.
-
Selvin over 12 yearsWhat is that barBackground.CGImage by the way? It seems nowhere else in the above code.
-
tt.Kilew over 12 yearsThis 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
-
Manni over 12 yearsI 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 :-)
-
tt.Kilew over 12 yearsIn case of iOS5 device you should place it in EVERY controller, that has new Navigation controller
-
JFMartin over 12 yearsThis 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?
-
tt.Kilew over 12 yearsbarBackground has UIImage class, so CGIImage just property wit CGImagRef of UIImage class.
-
Hot Licks over 12 yearsUnfortunately, the buttons disappear when you do this.
-
Layynezz about 12 yearswhat are the drawbacks of doing it this way (apart from ugliness?). Will Apple object?
-
tt.Kilew about 12 yearsAt 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:)
-
kheraud about 12 yearsGreat, working and simple when coming from the category solution and too lazy to go on a subclass trick.