How can I replicate UINavigationBar's gradient colors?

I’ve been trying to replicate the gradient from UINavigationBar to use as a gradient on custom UIButton subclass objects on the same view.

However, I can’t figure out how the colors are derived? That is, you only specify one color to set a UINavigationBar‘s background color- tintColor – but it creates a nice gradient with it seems at least 4 colors?

  • Turn off console logging for specific objects
  • How to set my web view loaded with already login user -iPhone
  • filtering single and double taps
  • Objective-C Singleton problem. Object recognized in one class but not another
  • “The binary you uploaded was invalid. the file was not a valid zip file” Error message uploading app to iTunes Connect
  • What is the iOS 5.0 user agent string?
  • I’m really just interested in the “inner” top and bottom colors though – just inside the 1px border around the bar… the outer “border” colors do indeed appear different though.

    EDIT – 1

    Upon further research, it appears the HSB (instead of the RBG as first thought) values are being manipulated to get these different colors.

    There is also a convenience method on UIColor to get the HSB values, which should be helpful:

    getHue:saturation:brightness:alpha:
    

    Helpful References Found So Far

    HSL and HSV Wiki

    UIColor Class Reference

    Programmatically Lighten a Color

    From the book Fundamentals of Interactive Computer Graphics

    EDIT – 2

    In case you were unaware that you could set a gradient for a background on a UIButton programmatically, here’s some references for how to do such:

    FUN WITH UIBUTTONS AND CORE ANIMATION LAYERS

    Five Tips for Creating Stylish UIButtons (kudos to @cdo for providing this link)

    EDIT – 3

    I’ve put together a spreadsheet showing the original and the “inner” gradient colors (disregarding the outer most colors) in HSB values on UINavigationBar and its corresponding “back” button (titles are irrelevant and always displayed white).

    Here’s a link to the Google doc with the information that I’ve collected for a few sample colors:

    https://docs.google.com/spreadsheet/ccc?key=0AnKVtzkNS9scdGVRN01pa1NQcC1hdThNbEVzQU8wRlE&usp=sharing

    Note: these values were found by saving a screenshot using the retina, 3.5″ iPhone Simulator (Xcode version 4.6) for iOS 6.1 and eye-dropping the HSB values using PhotoShop.

    BOUNTY AWARD CRITERIA

    I’ve opened a bounty on this question to bring more exposure to it and hopefully get a good answer. The answer that I’m looking for:

    Provide a method of calculating/closely approximating (in most cases) the RGB or HSB values of the “inner top” and “inner bottom” gradient colors (see spreadsheet) created after setting a tintColor on UINavigationBar.

    Bonus points awarded (above initial bounty offering) if you also provide a method for calculating the “inner top” and “inner bottom” gradient colors on the “back” button (which is similar to the navigation bar, but I’ve found these colors appear to be slightly “darker” usually)?

    4 Solutions Collect From Internet About “How can I replicate UINavigationBar's gradient colors?”

    Short Answer: it is not gradient

    Long Answer: After tint color applied, there is a transparent overlay image rendered on top of it.

    It is called: UITintedTopBarHighlight@2x.png an it is in UIKit artwork. (uploaded here: http://cl.ly/image/2c2V3t1D1T3L)

    It is 2×88 pixel image, and must be repeated horizontally over tinted background.

    For back button, it is very similar, but there is also a mask to give it it’s shape. UItintedBackButtonHighlight and UITintedBackButtonMask.

    It’s hard to copy the exact behavior because it seems that Apple is calculating different for different color groups. eg. a light color is slightly darkened while a dark color is lit up.

    Same for the bar button item. For some colors the difference for normal “bordered” button and “done”-style button is completely different. Sometimes not noticeable like for turquoise but definitely seeable for orange.

    For creating this sample code I used PaintCode a nice tool for prototyping btw..
    Copy this code in a UIView subclass or something. Or just grab the pieces code you need.

    - (void)drawRect:(CGRect)rect
    {
        //// General Declarations
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        //// Color Declarations
        UIColor* tint = [UIColor colorWithRed: 1 green: 0.66 blue: 0.329 alpha: 1];
        CGFloat tintRGBA[4];
        [tint getRed: &tintRGBA[0] green: &tintRGBA[1] blue: &tintRGBA[2] alpha: &tintRGBA[3]];
    
        UIColor* lighter = [UIColor colorWithRed: (tintRGBA[0] * 0.58 + 0.42) green: (tintRGBA[1] * 0.58 + 0.42) blue: (tintRGBA[2] * 0.58 + 0.42) alpha: (tintRGBA[3] * 0.58 + 0.42)];
        CGFloat lighterRGBA[4];
        [lighter getRed: &lighterRGBA[0] green: &lighterRGBA[1] blue: &lighterRGBA[2] alpha: &lighterRGBA[3]];
    
        UIColor* lightest = [UIColor colorWithRed: (lighterRGBA[0] * 0.55 + 0.45) green: (lighterRGBA[1] * 0.55 + 0.45) blue: (lighterRGBA[2] * 0.55 + 0.45) alpha: (lighterRGBA[3] * 0.55 + 0.45)];
        UIColor* darker = [UIColor colorWithRed: (tintRGBA[0] * 0.92) green: (tintRGBA[1] * 0.92) blue: (tintRGBA[2] * 0.92) alpha: (tintRGBA[3] * 0.92 + 0.08)];
        CGFloat darkerRGBA[4];
        [darker getRed: &darkerRGBA[0] green: &darkerRGBA[1] blue: &darkerRGBA[2] alpha: &darkerRGBA[3]];
    
        UIColor* darkest = [UIColor colorWithRed: (darkerRGBA[0] * 0.65) green: (darkerRGBA[1] * 0.65) blue: (darkerRGBA[2] * 0.65) alpha: (darkerRGBA[3] * 0.65 + 0.35)];
    
        //// Gradient Declarations
        NSArray* gradientColors = [NSArray arrayWithObjects:
                                   (id)lighter.CGColor,
                                   (id)darker.CGColor, nil];
        CGFloat gradientLocations[] = {0, 1};
        CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradientColors, gradientLocations);
    
    
        //// top Drawing
        UIBezierPath* topPath = [UIBezierPath bezierPathWithRect: CGRectMake(0, 0, rect.size.width, 1)];
        [lightest setFill];
        [topPath fill];
    
        //// theGradient Drawing
        UIBezierPath* theGradientPath = [UIBezierPath bezierPathWithRect: CGRectMake(0, 1, rect.size.width, rect.size.height - 1.0f)];
        CGContextSaveGState(context);
        [theGradientPath addClip];
        CGContextDrawLinearGradient(context, gradient, CGPointMake(50, 1), CGPointMake(50, rect.size.height-1.0f), 0);
        CGContextRestoreGState(context);
    
        //// bottom Drawing
        UIBezierPath* bottomPath = [UIBezierPath bezierPathWithRect: CGRectMake(0, rect.size.height-1.0f, rect.size.width, 1)];
        [darkest setFill];
        [bottomPath fill];
    
    
        //// Cleanup
        CGGradientRelease(gradient);
        CGColorSpaceRelease(colorSpace);
    }
    

    Thanks for the good question and the generous bounty. I went to work on this and neglected to check in to see that it was already answered acceptably. Nevertheless, it was fun building and testing the following navigation bar category, that reveals it’s colors …

    //
    //  UINavigationBar+colors.h
    
    #import <UIKit/UIKit.h>
    
    @interface UINavigationBar (Colors)
    
    // Answer an array of colors representing the color of the reciever, starting at fromY, up to toY
    - (NSArray *)colorsFromY:(NSUInteger)fromY to:(NSUInteger)toY;
    
    @end
    

    Link with QuartzCore.framework.

    //
    //  UINavigationBar+colors.m
    
    #import "UINavigationBar+colors.h"
    #import <QuartzCore/QuartzCore.h>
    
    #define UIIMAGE_BYTES_PER_PIXEL 4u
    
    @implementation UINavigationBar (Colors)
    
    + (NSData *)dataFromImage:(UIImage *)image {
    
        CGImageRef imageRef = [image CGImage];
        NSUInteger width = CGImageGetWidth(imageRef);
        NSUInteger height = CGImageGetHeight(imageRef);
        CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
        NSUInteger dataSize = height * width * UIIMAGE_BYTES_PER_PIXEL;
        unsigned char *rawData = malloc(dataSize);
        NSUInteger bytesPerRow = width * UIIMAGE_BYTES_PER_PIXEL;
        NSUInteger bitsPerComponent = 8;
        CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                     bitsPerComponent, bytesPerRow, colorSpace,
                                                     kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
        CGColorSpaceRelease(colorSpace);
    
        CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
        CGContextRelease(context);
    
        NSData *rtn = [NSData dataWithBytes:rawData length:dataSize];
        free(rawData);
        return rtn;
    }
    
    
    + (UIColor *)colorOfImage:(UIImage *)image atX:(NSUInteger)px atY:(NSUInteger)py  {
    
        NSData *imgData = [self dataFromImage:image];
        if (!imgData) return nil;
    
        NSUInteger byteIndex = UIIMAGE_BYTES_PER_PIXEL * (image.size.width * py + px);
    
        unsigned char rgbaData[4];
        NSRange range = { byteIndex, 4u };
        [imgData getBytes:rgbaData range:range];
        CGFloat red   = rgbaData[0] / 255.0;
        CGFloat green = rgbaData[1] / 255.0;
        CGFloat blue  = rgbaData[2] / 255.0;
        CGFloat alpha = rgbaData[3] / 255.0;
    
        return [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
    }
    
    - (UIImage *)asImage {
    
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, 0.0);
        [self.layer renderInContext:UIGraphicsGetCurrentContext()];
        UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        return img;
    }
    
    - (NSArray *)colorsFromY:(NSUInteger)fromY to:(NSUInteger)toY {
    
        NSMutableArray *answer = [NSMutableArray array];
        UIImage *image = [self asImage];
    
        for (NSUInteger y = MAX(0, fromY); y < MIN(self.bounds.size.height, toY); y++) {
            [answer addObject:[self.class colorOfImage:image atX:1 atY:y]];
        }
        return [NSArray arrayWithArray:answer];
    }
    
    @end
    

    Call it like this:

    // from a view controller contained by a navigation controller...
    UINavigationBar *bar = self.navigationController.navigationBar;
    
    NSArray *colors = [bar colorsFromY:0 to:bar.bounds.size.height];
    for (UIColor *color in colors) {
        NSLog(@"%@", color);
    }
    

    UIButton takes a single tintColor property but that doesn’t mean it isn’t computing other colors to use in the gradient behind the scenes. Try this tutorial.