Sunday, June 9, 2013

UINavigationBar backgroundImageForBarMetrics example in Objective C (iOS).


UINavigationBar backgroundImageForBarMetrics

Returns the background image for given bar metrics.

- (UIImage *)backgroundImageForBarMetrics:(UIBarMetrics)barMetrics

Parameters
barMetrics
A bar metrics constant.

Return Value of [UINavigationBar backgroundImageForBarMetrics]
The background image for barMetrics.

UINavigationBar backgroundImageForBarMetrics example.
UINavigationBar *appearanceProxBar = [UINavigationBar appearance];
UIImage *defaultImage = [appearanceProxBar backgroundImageForBarMetrics:UIBarMetricsDefault];
[appearanceProxBar setBackgroundImage:navBarTexture forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearanceWhenContainedIn:[UIPopoverController class], nil] setBackgroundImage:defaultImage forBarMetrics:UIBarMetricsDefault];

Example of [UINavigationBar backgroundImageForBarMetrics].
UINavigationBar+SYCustomBackground.h

#import <UIKit/UIKit.h>

@interface UINavigationBar (SYCustomBackground)
@property (nonatomic,retain) UIImage *sy_customBackgroundImage;
@end
UINavigationBar+SYCustomBackground.m

#import "UINavigationBar+SYCustomBackground.h"
#import <objc/runtime.h>

@implementation UINavigationBar (SYCustomBackground)

static char BACKGROUND_IMAGE_KEY;
static BOOL drawRectsSwizzled = NO;

// Swizzles drawRect: and sy_drawRect:
- (void)swizzleDrawRectIfNecessary
{
    if (!drawRectsSwizzled) {
        Method origMethod = class_getInstanceMethod([self class], @selector(drawRect:));
        Method myMethod = class_getInstanceMethod([self class], @selector(sy_drawRect:));
        method_exchangeImplementations(origMethod, myMethod);
        drawRectsSwizzled = YES;
    }
}

- (void)setSy_customBackgroundImage:(UIImage *)image
{
    // iOS 5
    if ([self respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) {
        [self setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
    }
    // iOS < 5
    else {
        [self swizzleDrawRectIfNecessary];
        objc_setAssociatedObject(self, &BACKGROUND_IMAGE_KEY, image, OBJC_ASSOCIATION_RETAIN);
    }
}

- (UIImage *)sy_customBackgroundImage
{
    // iOS 5
    if ([self respondsToSelector:@selector(setBackgroundImage:forBarMetrics:)]) {
        return [self backgroundImageForBarMetrics:UIBarMetricsDefault];
    }
    // iOS < 5
    else {
        [self swizzleDrawRectIfNecessary];
        return objc_getAssociatedObject(self, &BACKGROUND_IMAGE_KEY);
    }
}

- (void)sy_drawRect:(CGRect)rect
{
    UIImage *backgroundImage = self.sy_customBackgroundImage;

    if (backgroundImage) {
        [backgroundImage drawInRect:rect];
    }
    else {
        // No custom image, calling original drawRect:
        // Note: it’s swizzled, so we must call sy_drawRect:
        [self sy_drawRect:rect];
    }
}

@end
Then you set your background images in viewWillAppear and restore them in viewWillDisappear if you want to change images in one UINavigationController.

It’s not perfect, I would add some crossfade transition, add push/pop for backgrounds, but I have no time yet, so feel free to improve it.

End of UINavigationBar backgroundImageForBarMetrics example article.