如何复制UINavigationBar渐变颜色?

时间:2013-03-08 22:46:39

标签: iphone ios cocoa

我一直在尝试从UINavigationBar复制渐变,以在同一视图上的自定义UIButton子类对象上用作渐变。

然而,我无法弄清楚颜色是如何衍生出来的?也就是说,您只需指定一种颜色来设置UINavigationBar的背景颜色 - tintColor - 但它会创建一个漂亮的渐变,似乎至少有4种颜色?

我真的只对“内部”顶部和底部颜色感兴趣 - 只是在酒吧周围的1px边框内...外部“边框”颜色确实看起来不同。

编辑 - 1

经过进一步研究,看来HSB(而非首次考虑的RBG)值被操纵以获得这些不同的颜色。

UIColor上还有一种方便的方法来获取HSB值,这应该会有所帮助:

getHue:saturation:brightness:alpha:

迄今为止发现的有用参考资料

HSL and HSV Wiki

UIColor Class Reference

Programmatically Lighten a Color

From the book Fundamentals of Interactive Computer Graphics

编辑 - 2

如果您不知道可以通过编程方式在UIButton上为背景设置渐变,请参考以下有关如何执行此操作的参考:

FUN WITH UIBUTTONS AND CORE ANIMATION LAYERS

Five Tips for Creating Stylish UIButtons(感谢@cdo提供此链接)

编辑 - 3

我在UINavigationBar及其相应的“后退”按钮上放置了显示原始和“内部”渐变颜色(忽略最外面的颜色)的电子表格(标题不相关并且始终显示)白色)。

以下是Google文档的链接,其中包含我为一些示例颜色收集的信息:

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

注意:通过使用视网膜保存屏幕截图,使用适用于iOS 6.1的3.5英寸iPhone模拟器(Xcode版本4.6)以及使用PhotoShop关注HSB值,可以找到这些值。

BOUNTY AWARD CRITERIA

我已经在这个问题上开了一笔赏金,以便更多地接触它,并希望得到一个好的答案。我正在寻找答案:

提供一种计算/近似(大多数情况下)在tintColor上{UINavigationBar后创建的“内部顶部”和“内部底部”渐变颜色(参见电子表格)的RGB或HSB值的方法。 1}}。

如果您还提供了计算“返回”按钮上的“内部顶部”和“内部底部”渐变颜色的方法(类似于导航栏,但是我',我们发现这些颜色通常会略微“变暗”吗?

4 个答案:

答案 0 :(得分:7)

简答:它不是渐变

长答案:应用色调颜色后,会在其上呈现透明的叠加图像。

它被称为:UITintedTopBarHighlight@2x.png它在UIKit艺术品中。 (上传到此处:http://cl.ly/image/2c2V3t1D1T3L

它是2x88像素图像,必须在有色背景上水平重复。

对于后退按钮,它非常相似,但也有一个遮罩来赋予它它的形状。 UItintedBackButtonHighlight和UITintedBackButtonMask。

答案 1 :(得分:2)

很难复制确切的行为,因为Apple似乎为不同的颜色组计算不同。例如。当浅色点亮时,浅色稍微变暗。

条形按钮项目相同。对于某些颜色,普通“有边界”按钮和“完成”式按钮的区别完全不同。有时不像绿松石那样明显,但绝对可以看到橙色。

为了创建这个示例代码,我使用了PaintCode一个很好的原型btw工具。
将此代码复制到UIView子类或其他内容中。或者只是抓住你需要的部分代码。

- (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);
}

答案 2 :(得分:2)

感谢好问题和慷慨的赏金。我开始研究这个问题而忽略了检查它是否已经得到了可接受的回答。然而,构建和测试以下导航栏类别很有趣,它显示了它的颜色......

//
//  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

与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

这样称呼:

// 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);
}

答案 3 :(得分:0)

UIButton采用单个tintColor属性,但这并不意味着它不会计算在幕后渐变中使用的其他颜色。 Try this tutorial