检查UIColor是黑暗还是明亮?


107

我需要确定所选的UIColor(由用户选择)是深色还是明亮,因此我可以更改位于该颜色之上的一行文本的颜色,以提高可读性。

这是Flash / ActionScript(带有演示)中的示例:http ://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

有什么想法吗?

干杯,安德烈

更新

感谢大家的建议,以下是工作代码:

- (void) updateColor:(UIColor *) newColor
{
    const CGFloat *componentColors = CGColorGetComponents(newColor.CGColor);

    CGFloat colorBrightness = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
    if (colorBrightness < 0.5)
    {
        NSLog(@"my color is dark");
    }
    else
    {
        NSLog(@"my color is light");
    }
}

再次感谢 :)


似乎有些颜色没有3个成分,例如UIColor.black。
格伦·豪斯

Answers:


75

W3C具有以下内容:http : //www.w3.org/WAI/ER/WD-AERT/#color-contrast

如果您只使用黑色或白色文字,请使用上面的颜色亮度计算。如果低于125,请使用白色文字。如果大于或等于125,请使用黑色文字。

编辑1:偏向黑色文字。:)

编辑2:使用的公式是((红色值* 299)+(绿色值* 587)+(蓝色值* 114))/ 1000。


您知道如何获取颜色的红色,绿色和蓝色值吗?
安德烈(Andre)

您可以NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);用来获取颜色分量的数组,包括alpha。文档没有指定它们的顺序,但是我假设它将是红色,绿色,蓝色,alpha。另外,根据文档,“数组的大小比该颜色的颜色空间的组件数大一倍。” -它没有说明原因...
Jasarien

这很奇怪...使用:NSArray * components =(NSArray *)CGColorGetComponents(newColor.CGColor); NSLog(@“我的颜色组件%f%f%f%f”,components [0],components [1],components [2],components [3]);只是看我是否可以获取这些值,似乎只有0索引发生了变化,而无论我选择哪种颜色,其他的都将保持不变。知道为什么吗?
安德烈(Andre)2010年

得到它了。您不能使用NSArray。改用它:const CGFloat * components = CGColorGetComponents(newColor.CGColor); 同样,顺序是:红色是components [0];绿色是组件[1];蓝色是组件[2];alpha是components [3];
安德烈(Andre)2010年

啊,我的坏。我以为它返回的是CFArrayRef,而不是C数组。抱歉!
Jasarien

44

这是执行此检查的Swift(3)扩展。

此扩展程序适用于灰度颜色。但是,如果要使用RGB初始化程序创建所有颜色,而不使用诸如UIColor.black和的内置颜色UIColor.white,则可以删除其他检查。

extension UIColor {

    // Check if the color is light or dark, as defined by the injected lightness threshold.
    // Some people report that 0.7 is best. I suggest to find out for yourself.
    // A nil value is returned if the lightness couldn't be determined.
    func isLight(threshold: Float = 0.5) -> Bool? {
        let originalCGColor = self.cgColor

        // Now we need to convert it to the RGB colorspace. UIColor.white / UIColor.black are greyscale and not RGB.
        // If you don't do this then you will crash when accessing components index 2 below when evaluating greyscale colors.
        let RGBCGColor = originalCGColor.converted(to: CGColorSpaceCreateDeviceRGB(), intent: .defaultIntent, options: nil)
        guard let components = RGBCGColor?.components else {
            return nil
        }
        guard components.count >= 3 else {
            return nil
        }

        let brightness = Float(((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000)
        return (brightness > threshold)
    }
}

测试:

func testItWorks() {
    XCTAssertTrue(UIColor.yellow.isLight()!, "Yellow is LIGHT")
    XCTAssertFalse(UIColor.black.isLight()!, "Black is DARK")
    XCTAssertTrue(UIColor.white.isLight()!, "White is LIGHT")
    XCTAssertFalse(UIColor.red.isLight()!, "Red is DARK")
}

注意:已更新为Swift 3 12/7/18


6
效果很好。使用Swift 2.0-亮度计算时出现编译器错误:“表达式太复杂,无法在合理的时间内解决;请考虑将表达式分解为不同的子表达式”。我通过添加以下类型来修复该问题:“让亮度=(作为CGFloat的((components [0] * 299.0)* +(作为CGFloat的(components [1] * 587.0))+(作为CGFloat的([components [2] * 114.0)) )/(1000.0 as CGFloat)”
GK100

1
我在Swift 2.1中也遇到了同样的问题。我通过简单地为组件创建变量而不是使用来访问变量来解决了这个问题components[0]。像这样:let componentColorX: CGFloat = components[1]
petritz 2015年

1
Xcode告诉我亮度=“表达式太复杂,无法在合理的时间内解决;请考虑将表达式分解为不同的子表达式”
daidai

代代,只是简化表达即可。最后的划分是不必要的。我们不需要在0-1的范围内工作。不要除以1000,而只需检查该值是否大于500。
aronspring '16

32

使用Erik Nedwidek的答案,我想出了那小段代码以便于包含。

- (UIColor *)readableForegroundColorForBackgroundColor:(UIColor*)backgroundColor {
    size_t count = CGColorGetNumberOfComponents(backgroundColor.CGColor);
    const CGFloat *componentColors = CGColorGetComponents(backgroundColor.CGColor);

    CGFloat darknessScore = 0;
    if (count == 2) {
        darknessScore = (((componentColors[0]*255) * 299) + ((componentColors[0]*255) * 587) + ((componentColors[0]*255) * 114)) / 1000;
    } else if (count == 4) {
        darknessScore = (((componentColors[0]*255) * 299) + ((componentColors[1]*255) * 587) + ((componentColors[2]*255) * 114)) / 1000;
    }

    if (darknessScore >= 125) {
        return [UIColor blackColor];
    }

    return [UIColor whiteColor];
}

我使用此代码,效果很好,但是不知道当我设置[UIColor blackColor]时,它返回darkScore = 149.685。你能解释为什么会这样吗?
DivineDesert 2014年

3
此代码不适用于非RGB颜色,例如UIColor whiteColor, grayColor, blackColor
rmaddy14年

@rmaddy为什么?还是为什么白色,灰色和黑色不是RGB颜色?
mattsven 2015年

1
@rmaddy我如何以编程方式分辨出RGB颜色空间中的颜色与灰度之间的区别?
mattsven 2015年

1
@maddy没关系!感谢您的帮助- stackoverflow.com/questions/1099569/...
mattsven

31

迅捷3

extension UIColor {
    var isLight: Bool {
        var white: CGFloat = 0
        getWhite(&white, alpha: nil)
        return white > 0.5
    }
}

// Usage
if color.isLight {
    label.textColor = UIColor.black
} else {
    label.textColor = UIColor.white
}

对我来说,这是最好的。您甚至可以在支票中设置白色范围以适合您的需求,并且超级简单且原生。谢谢。
Andreas777 '17

9

Swift 4版本

extension UIColor {
    func isLight() -> Bool {
        guard let components = cgColor.components, components.count > 2 else {return false}
        let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000
        return (brightness > 0.5)
    }
}

1
这可能不适用于默认的系统颜色,例如UIColor.white,因为它只有2个成分,不是RGB表示形式。
Skrew,

7

我对这个问题的解决方案归类(从此处的其他答案中得出)。也适用于灰度颜色,在撰写本文时,其他答案均不起作用。

@interface UIColor (Ext)

    - (BOOL) colorIsLight;

@end

@implementation UIColor (Ext)

    - (BOOL) colorIsLight {
        CGFloat colorBrightness = 0;

        CGColorSpaceRef colorSpace = CGColorGetColorSpace(self.CGColor);
        CGColorSpaceModel colorSpaceModel = CGColorSpaceGetModel(colorSpace);

        if(colorSpaceModel == kCGColorSpaceModelRGB){
            const CGFloat *componentColors = CGColorGetComponents(self.CGColor);

            colorBrightness = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
        } else {
            [self getWhite:&colorBrightness alpha:0];
        }

        return (colorBrightness >= .5f);
    }

@end

很好地处理非RGB颜色-就像魅力一样。
hatunike

5

更简单的Swift 3扩展:

extension UIColor {
    func isLight() -> Bool {
        guard let components = cgColor.components else { return false }
        let redBrightness = components[0] * 299
        let greenBrightness = components[1] * 587
        let blueBrightness = components[2] * 114
        let brightness = (redBrightness + greenBrightness + blueBrightness) / 1000
        return brightness > 0.5
    }
}

2
这可能不适用于默认的系统颜色,例如UIColor.white,因为它只有2个成分,不是RGB表示形式。
Skrew,

真正!您可以将颜色转换为十六进制字符串,然后返回到UIColor来解决。
桑卡斯'18

3

如果您更喜欢块版本:

BOOL (^isDark)(UIColor *) = ^(UIColor *color){
    const CGFloat *component = CGColorGetComponents(color.CGColor);
    CGFloat brightness = ((component[0] * 299) + (component[1] * 587) + (component[2] * 114)) / 1000;

    if (brightness < 0.75)
        return  YES;
    return NO;
};

2

对于所有不偏灰的事物,颜色的RGB反色通常会与之形成强烈对比。该演示仅反转颜色并将其去饱和(将其转换为灰色)。

但是,要生成一种令人舒缓的颜色组合非常复杂。看着 :

http://particletree.com/notebook/calculating-color-contrast-for-legible-text/


1
如果只有iPhone版本的话:D
Andre

因此,如果我获得了某种颜色的RGB值(我不知道该怎么做),并使用这些值的倒数创建了一种新的颜色,那应该可以在非常基本的水平上工作吗?
安德烈(Andre)2010年

2

下面的方法是基于白色的颜色在Swift语言中找到浅色或深色。

func isLightColor(color: UIColor) -> Bool 
{
   var white: CGFloat = 0.0
   color.getWhite(&white, alpha: nil)

   var isLight = false

   if white >= 0.5
   {
       isLight = true
       NSLog("color is light: %f", white)
   }
   else
   {
      NSLog("Color is dark: %f", white)
   }

   return isLight
}

以下方法是使用颜色分量在Swift中找到颜色是浅色还是深色。

func isLightColor(color: UIColor) -> Bool 
{
     var isLight = false

     var componentColors = CGColorGetComponents(color.CGColor)

     var colorBrightness: CGFloat = ((componentColors[0] * 299) + (componentColors[1] * 587) + (componentColors[2] * 114)) / 1000;
     if (colorBrightness >= 0.5)
     {
        isLight = true
        NSLog("my color is light")
     }
     else
     {
        NSLog("my color is dark")
     }  
     return isLight
}

2

对我来说,仅使用CGColorGetComponents无效,我获得了2种UIColors组件,例如白色。因此,我必须先检查颜色spaceModel。这就是我想出的最终是@mattsven答案的快速版本。

从此处获取的色彩空间:https : //stackoverflow.com/a/16981916/4905076

extension UIColor {
    func isLight() -> Bool {
        if let colorSpace = self.cgColor.colorSpace {
            if colorSpace.model == .rgb {
                guard let components = cgColor.components, components.count > 2 else {return false}

                let brightness = ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000

                return (brightness > 0.5)
            }
            else {
                var white : CGFloat = 0.0

                self.getWhite(&white, alpha: nil)

                return white >= 0.5
            }
        }

        return false
    }

2

UIColor具有以下转换为HSB颜色空间的方法

- (BOOL)getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha;

1
- (BOOL)isColorLight:(UIColor*)color
{
    CGFloat white = 0;
    [color getWhite:&white alpha:nil];
    return (white >= .85);
}

新增Swift 5版本:

var white: CGFloat = 0.0
color.getWhite(&white, alpha: nil)
return white >= .85 // Don't use white background

1
仅当UIColor是灰度颜色时才有效。此代码无法使用随机的RGB颜色。
rmaddy14年

0

如果要查找颜色的亮度,请使用以下伪代码:

public float GetBrightness(int red, int blue, int green)
{
    float num = red / 255f;
    float num2 = blue / 255f;
    float num3 = green / 255f;
    float num4 = num;
    float num5 = num;
    if (num2 > num4)
        num4 = num2;
    if (num3 > num4)
        num4 = num3;
    if (num2 < num5)
        num5 = num2;
    if (num3 < num5)
        num5 = num3;
    return ((num4 + num5) / 2f);
}

如果大于0.5,则为亮,否则为暗。


2
您不能均等地加权每种颜色。我们的眼睛偏向于蓝色,偏向于偏向红色。
Erik Nedwidek 2010年

@ErikNedwidek:公平地说,这个问题只是询问颜色的亮度:)
Bryan Denny
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.