5

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

但是,我无法弄清楚颜色是如何派生的?也就是说,您只指定一种颜色来设置 aUINavigationBar的背景颜色tintColor- 但它创建了一个漂亮的渐变,看起来至少有 4 种颜色?

不过,我真的只是对“内部”顶部和底部颜色感兴趣——就在条形周围的 1px 边框内……虽然外部“边框”颜色确实看起来不同。

编辑 - 1

经过进一步研究,似乎 HSB(而不是最初认为的 RBG)值被操纵以获得这些不同的颜色。

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

getHue:saturation:brightness:alpha:

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

HSL 和 HSV 维基

UIColor 类参考

以编程方式淡化颜色

摘自《交互式计算机图形学基础》一书

编辑 - 2

如果您不知道可以以UIButton编程方式为背景设置渐变,这里有一些关于如何执行此操作的参考:

有趣的 UI 按钮和核心动画层

创建时尚 UIButtons 的五个技巧(感谢 @cdo 提供此链接)

编辑 - 3

我整理了一个电子表格,显示了 HSB 值中的原始和“内部”渐变颜色(不考虑最外面的颜色)UINavigationBar及其相应的“后退”按钮(标题无关紧要,始终显示为白色)。

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

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

注意:这些值是通过使用针对 iOS 6.1 的 3.5 英寸 iPhone 模拟器(Xcode 版本 4.6)保存屏幕截图并使用 PhotoShop 观察 HSB 值而找到的。

赏金标准

我已经在这个问题上开了一个赏金,以带来更多的曝光率,并希望得到一个好的答案。我正在寻找的答案:

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

如果您还提供了一种计算“后退”按钮(类似于导航栏,但我发现了这些颜色通常看起来稍微“更暗”)?

4

4 回答 4

7

简短答案:它不是渐变

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

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

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

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

于 2013-03-11T20:16:54.437 回答
2

很难复制确切的行为,因为 Apple 似乎在为不同的颜色组计算不同的值。例如。浅色略微变暗,而深色则亮起。

栏按钮项也是如此。对于某些颜色,普通“带边框”按钮和“完成”样式按钮的区别是完全不同的。有时不像绿松石那样引人注目,但对于橙色来说绝对是可见的。

为了创建这个示例代码,我使用了PaintCode一个很好的原型设计工具。
将这个代码复制到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);
}
于 2013-03-11T10:24:32.887 回答
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);
}
于 2013-03-12T02:46:02.923 回答
0

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

于 2013-03-09T02:14:14.623 回答