1
votes

There is a custom UIImageView under "Black Translucent" UINavigationBar, but I'm not too happy with the result. The custom image is not visible enough and while changing navigation bar alpha value brings it more visible, it makes the navigation bar title and buttons less visible.

Question: I want a fully visible custom UINavigationBar background image with fully visible bar title and buttons. Any ideas how would I get all of that?

Update: My solution was similar as these answers, but a bit different. Don't want to accept my own answer as correct one, so blogged about it (link) at iLessons iLearned.

5
I can use UILabel for nice "text visible, background transparent" but still would need buttons...JOM

5 Answers

3
votes

I wanted a custom UINavigationBar background image too, and ended up solving it by:

  • In the UINavigationController's initialisation, I created a CALayer that had contents set to the background image, and the frame set accordingly.
  • I added that layer to the UINavigationBar's layer (at index 0, in the background): [self.navigationBar.layer insertSublayer:imageLayer atIndex:0]

At this point, it looks okay at first, but if you do any navigation, labels and buttons disappear behind the 'background' layer. So,

  • I created a subclass of CALayer that changed the behaviour of insertSublayer:atIndex: so that no layers are inserted beneath the background layer.

Like this:

 @implementation CTNavigationBarLayer 
 - (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx {
     if ( idx == 0 ) idx = 1;
     [super insertSublayer:layer atIndex:idx];
 }
 - (void)addBackgroundLayer:(CALayer*)layer {
     [super insertSublayer:layer atIndex:0];
 }
 @end
  • Using a category on UINavigationBar, I overloaded the +layerClass method to return [CTNavigationBar class], so the navigation bar uses the custom layer.

Of course, this changes the behaviour of all UINavigationBars in your app, so if this isn't what you want, you'd want to seek an alternate way. You could subclass UINavigationBar, but then the only way to get that subclass to be used in a UINavigationController is via IB (or do some rather interesting mucking about to do it programmatically)

  • Finally, back in the UINavigationController initialisation, I replaced the layer insertion with a call to the addBackgroundLayer: method I wrote.
2
votes

Using drawRect in a category for UINavigationBar won't work in iOS5. The way suggested by Apple is to create a subclass of UINavigationBar and use it in your NIB file.

2
votes

For the benefit of people coming along here a bit later, it's worth noting that iOS5 provides direct support for this kind of customization. To change the image underlying the UINavigationBar, use

- (void)setBackgroundImage:(UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics

There's also a property

@property(nonatomic, copy) NSDictionary *titleTextAttributes

for adjusting the title. And you can use appearance proxies to make these changes pervasive in your app. More discussion in WWDC 2011, Session 114, "Customizing the Appearance of UIKit Controls".

Before iOS5, the way you were supposed to it was by subclassing UINavigationBar.

1
votes

You can also create a UINavigationBar category class and override the drawLayer:inContext: method. Inside the drawLayer:inContext: method, you can draw the background image you want to use.

- (void) drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    if ([self isMemberOfClass:[UINavigationBar class]] == NO) {
        return;
    }

    UIImage *image = (self.frame.size.width > 320) ?
                        [UINavigationBar bgImageLandscape] : [UINavigationBar bgImagePortrait];
    CGContextClip(context);
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextDrawImage(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height), image.CGImage);
}

For a complete demo Xcode project on customizing the appearance of UINavigationBar this and this might be helpful.

0
votes

The ardalahmet solution is not the right one if you need different backgrounds for each UINavigationBar when you have more than one UINavigationController, such as in a UITabBar, because it using UINavigationBar categories.