14

I just switched over to iOS 5 and everything appears to be working in my application aside from the custom navigation bar. I looked around and followed everybody's suggestion of calling the new methods setBackgroundImage: forBarMetrics: however it doesn't appear to work. This is the code I've tried to place both within the app delegate and within the viewDidLoad method of some of the view controllers:

UINavigationBar *nb = [[UINavigationBar alloc]init];
if( [nb respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)] )
{
    UIImage *image = [UIImage imageNamed:@"navBarBackground.png"];
    [nb setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
}
[nb release];

Unfortunately this doesn't work. If anybody has any suggestions at all, I'm all ears!

Nick ONeill
  • 7,341
  • 10
  • 47
  • 61

6 Answers6

44

To apply image to all your navigation bars, use the appearance proxy:

[[UINavigationBar appearance] setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];

For an individual bar:

// Assuming "self" is a view controller pushed on to a UINavigationController stack
[self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];

In your example, the background image won't change because nb isn't hooked up to anything.

Rob Bajorek
  • 6,382
  • 7
  • 44
  • 51
  • 1
    Note that this will only work for devices running iOS 5. If you need to have backward compatibility to 4.x you will have to figure something else out. – RPeck Oct 17 '11 at 17:09
  • 8
    on iOS 7 this appears to be doing some really weird tiling – powerj1984 Sep 25 '13 at 19:54
  • @Odelya I re-did my background graphic to fit exactly, I think 320x64 off the top of my head – powerj1984 Oct 06 '13 at 15:15
  • Yes, this makes strange tiling in the navigation bar. the image width is less than 320. I want it centered. any solution? – karim May 12 '14 at 07:32
  • 1
    For all these methods, you need exactly 320x44/64 image. Otherwise iOS creates strange tiling! – karim May 12 '14 at 07:44
4

Answer by rob is correct but if application runs on iOS 4.3 or lower app will crash. So you can implement this like

if([[UINavigationBar class] respondsToSelector:@selector(appearance)]) //iOS >=5.0
{
    [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"navigationBar.png"] forBarMetrics:UIBarMetricsDefault];
    [[UINavigationBar appearance] setBackgroundImage:[UIImage imageNamed:@"navBar-Landscape.png"] forBarMetrics:UIBarMetricsLandscapePhone];   

}

This will set image for both mode Landscape and Portrait

Kamleshwar
  • 2,967
  • 1
  • 25
  • 27
  • Unfortunately this does not work. The proper solution is this: if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) { //code } – Borut Tomazin Jan 26 '12 at 14:20
  • @BorutTomazin: This does actually work, quite well! You could do a further test to see if [UINavigationBar appearance] responds to selector `setBackgroundImage:forBarMetrics:`, but this is the 'first stage' in testing that UINavigationBar first responds to the `appearance` method (iOS5 only). – Chris Nolet Mar 13 '12 at 07:53
  • Thanks sir, your code snippet was of great help to me. And thanks once again that because of you i have iOS development as my career. A vote up from me :) – Arshad Parwez Jan 22 '13 at 11:39
3

You need to get the navigation bar from a navigation controller. Right now you are just creating one and then it deallocs when you release it. You need to get the navigation controller for your view controller.

UINavigationController * navigationController = [self navigationController];
UINavigationBar * nb = [navigationController navigationBar];
RPeck
  • 448
  • 3
  • 9
3

Note that if you want to apply the same to a toolbar, it is slightly different. Here's an example, building on Rob's answer:

    [[UIToolbar appearance] setBackgroundImage:[UIImage imageNamed:@"NavBarDarkWoodGrain.png"] forToolbarPosition:UIToolbarPositionBottom barMetrics:UIBarMetricsDefault];
DenVog
  • 4,226
  • 3
  • 43
  • 72
0

You didn't do something like use a UINavigationBar subclass? If you do this and override drawRect: on iOS5 and above, things will break.

See my answer here for a way to support both OS versions.

Community
  • 1
  • 1
Paul de Lange
  • 10,613
  • 10
  • 41
  • 56
0

This worked for me.

UIView *parentViewLeft = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
parentViewLeft.backgroundColor = [UIColor clearColor];

if ([self.navigationController.navigationBar respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) {
    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"navbar.png"]
                                                  forBarMetrics:UIBarMetricsDefault];    
} else {
    UIImageView* imgBG = [[UIImageView alloc]init];
    imgBG.image = [UIImage imageNamed:@"navbar.png"];
    imgBG.frame = CGRectMake(-10,0, 330, 44);
    [parentViewLeft addSubview:imgBG];
}

UIBarButtonItem *customBarButtomLeft = [[UIBarButtonItem alloc] initWithCustomView:parentViewLeft];
self.navigationItem.leftBarButtonItem = customBarButtomLeft;
matsr
  • 4,302
  • 3
  • 21
  • 36
Sunil Adhyaru
  • 581
  • 6
  • 18