Petko Yanakiev
Petko Yanakiev

Reputation: 159

Changing background image of UINavigationBar in ios < 5

I have a problem to change the background image of a UINavigationBar for IOS version < 5. I read already about one good solution, which is based on method swizzling, but the problem of this solution is when I add the image it covers everything include the buttons on a navigation bar. I found a solution which partially worked for me it is base on a following code:

@interface UINavigationBar (UINavigationBarCategory)
-(void)setBackgroundImage:(UIImage*)image withTag:(NSInteger)bgTag;
-(void)resetBackground:(NSInteger)bgTag; 
@end

@implementation UINavigationBar (UINavigationBarCategory)

-(void)setBackgroundImage:(UIImage*)image withTag:(NSInteger)bgTag{
if(image == NULL){ //might be called with NULL argument
    return;
}
UIImageView *aTabBarBackground = [[UIImageView alloc]initWithImage:image];
aTabBarBackground.frame = CGRectMake(0,0,self.frame.size.width,self.frame.size.height);
aTabBarBackground.tag = bgTag;
[self addSubview:aTabBarBackground];
//[self sendSubviewToBack:aTabBarBackground];
[aTabBarBackground release];
}
-(void)setRightButton:(UIButton*)button withTag:(NSInteger)bgTag{
if(button == NULL){ //might be called with NULL argument
    return;
}
    [self addSubview:button];
}

/* input: The tag you chose to identify the view */
-(void)resetBackground:(NSInteger)bgTag {
[self sendSubviewToBack:[self viewWithTag:bgTag]];
}
@end

I used this Category in my ViewWillAppear methods like this:

-(void) viewWillAppear:(BOOL)animated {
 UIImage *backgroundImage = [UIImage imageNamed:@"background_confernce_import_logo"];

if ([self.navigationController.navigationBar  respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)])
{
    [self.navigationController.navigationBar setBackgroundImage:backgroundImage forBarMetrics:UIBarMetricsDefault];
}
else{

   [[self.navigationController navigationBar] setBackgroundImage:backgroundImage     withTag:8675309];
}
}

In else clause I call setBackgroundImage. It is ok, but the problem is that if I have a right button on navigation bar of page 1 for example and go to page 2 after come back to page 1 the button is disappear. I should change the background image of navigation bar in every page in my application like this in viewWillAppear method where I put the new image. Any help will be appreciated. Under IOS 5 there are no such problem, but it should work on both versions.

Upvotes: 2

Views: 1566

Answers (2)

Till
Till

Reputation: 27587

As per request, here comes my slightly naughty and not really polished hack. This is really just for making the OP happy. The right answer was given by sergio.


UINavigationBar+CustomDraw.m

NSString *gNavbarBackgroundImageName = @"default_navbar_background.png";

@implementation UINavigationBar (CustomBackground)

- (void)drawRect:(CGRect)rect 
{ 
    if (gNavbarBackgroundImageName != nil)
    {
        UIImage *image = [UIImage imageNamed:gNavbarBackgroundImageName]; 
        [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    }
}
@end

UINavigationBar+CustomDraw.h

extern NSString *gNavbarBackgroundImageName;

And here comes the example usage in two view controllers...


FooViewController.m

#import "UINavigationBar+CustomDraw.h"

@implementation FooViewController

- (void)viewWillAppear:(BOOL)animated
{
    gNavbarBackgroundImageName = @"foo_navbar_background.png";
    [self.navigationController.navigationBar setNeedsDisplay];
}

@end

BarViewController.m

#import "UINavigationBar+CustomDraw.h"

@implementation BarViewController

- (void)viewWillAppear:(BOOL)animated
{
    gNavbarBackgroundImageName = @"bar_navbar_background.png";
    [self.navigationController.navigationBar setNeedsDisplay];
}

@end

Now let us assume you want to show a non-styled navigation bar, for example when displaying a Facebook login page (as provided by their SDK).

Use this to prevent any custom drawing:

gNavbarBackgroundImageName = nil;

Note Some of Apple's components use the UINavigationBar at places you might not have thought of. For example, the MPMoviePlayerController uses a custom navigation bar for displaying the upper part of its UI - so that would be another case where you want to prevent custom drawing.

Upvotes: 0

sergio
sergio

Reputation: 69027

I hate to say it, but your approach (adding a subview to hold the background) will not work exactly for the reason you mention. Each time the navigation bar is redrawn, the subview will not keep its z-order (and thus it will cover other UI elements). This behavior is described by other sources (see this, e.g.)

If you don't want to use swizzling, you could override drawRect in a category, so that the background is always drawn correctly. (this last option has the drawback that any navigation bar in your app will be drawn with the same background). This is a sample code I use:

@implementation UINavigationBar (CustomBackground)

 - (void)drawRect:(CGRect)rect { 
    UIImage *image = [UIImage imageNamed: @"back.png"]; 
    [image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
 }
@end

A different approach could be:

  1. subclassing UINavigationBar;
  2. overriding drawRect;
  3. in Interface Builder, set the class of your navigation bar object to your UINavigationBar subclass.

I haven't tried it, but it should work.

Upvotes: 1

Related Questions