I\'ve been trying to replicate the gradient from UINavigationBar
to use as a gradient on custom UIButton
subclass objects on the same view.
How
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);
}
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.
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);
}
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 2x88 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.