UINavigationBar's drawRect is not called in iOS 5.0

14,697

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://www.mladjanantic.com/setting-custom-background-for-uinavigationbar-what-will-work-on-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).

Share:
14,697
tt.Kilew
Author by

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, 2022

Comments

  • tt.Kilew
    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
    Matteo Alessani over 12 years
    drawRect code should be on MyNavigationBar implementation, not in the Category.
  • Selvin
    Selvin over 12 years
    What is that barBackground.CGImage by the way? It seems nowhere else in the above code.
  • tt.Kilew
    tt.Kilew over 12 years
    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
  • Manni
    Manni over 12 years
    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 :-)
  • tt.Kilew
    tt.Kilew over 12 years
    In case of iOS5 device you should place it in EVERY controller, that has new Navigation controller
  • JFMartin
    JFMartin over 12 years
    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?
  • tt.Kilew
    tt.Kilew over 12 years
    barBackground has UIImage class, so CGIImage just property wit CGImagRef of UIImage class.
  • Hot Licks
    Hot Licks over 12 years
    Unfortunately, the buttons disappear when you do this.
  • Layynezz
    Layynezz about 12 years
    what are the drawbacks of doing it this way (apart from ugliness?). Will Apple object?
  • tt.Kilew
    tt.Kilew about 12 years
    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:)
  • kheraud
    kheraud about 12 years
    Great, working and simple when coming from the category solution and too lazy to go on a subclass trick.