Andre
Andre

Reputation: 4487

Check if UIColor is dark or bright?

I need to determine whether a selected UIColor (picked by the user) is dark or bright, so I can change the color of a line of text that sits on top of that color, for better readability.

Here's an example in Flash/Actionscript (with demo): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

Any thoughts?

Cheers, Andre

UPDATE

Thanks to everyone's suggestions, here's the working code:

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

Thanks once again :)

Upvotes: 127

Views: 33894

Answers (16)

sabiland
sabiland

Reputation: 2614

Another, little bit faster version:

public extension UIColor {
    
    var getHsba: (h: CGFloat, s: CGFloat, b: CGFloat, a: CGFloat) {
        var h: CGFloat = 0, s: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
        self.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
        return (h: h, s: s, b: b, a: a)
    }

    func isLight() -> Bool {
        return self.getHsba.b > 0.5
    }
}

Upvotes: 0

Florentin Lupascu
Florentin Lupascu

Reputation: 993

Swift 5

extension UIColor {
    var isLight: Bool {
        if let components = self.cgColor.components {
            let firstComponent = (components[0] * 299)
            let secondComponent = (components[1] * 587)
            let thirdComponent = (components[2] * 114)
            
            let brightness = (firstComponent + secondComponent + thirdComponent) / 1000
            
            if brightness <= 0.5
            {
                return false
            }
            else
            {
                return true
            }
        }
        return false
    }
}

Upvotes: 0

Mike Glukhov
Mike Glukhov

Reputation: 1830

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

Added Swift 5 version:

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

Upvotes: 0

Cuddy
Cuddy

Reputation: 46

UIColor has the following method to convert to HSB color space:

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

Upvotes: 2

josh-fuggle
josh-fuggle

Reputation: 3157

Here is a Swift (3) extension to perform this check.

This extension works with greyscale colors. However, if you are creating all your colors with the RGB initializer and not using the built in colors such as UIColor.black and UIColor.white, then possibly you can remove the additional checks.

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

Tests:

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")
}

Note: Updated to Swift 3 12/7/18

Upvotes: 61

Lucho
Lucho

Reputation: 1044

For me using only CGColorGetComponents didn't worked, I get 2 components for UIColors like white. So I have to check the color spaceModel first. This is what I came up with that ended up being the swift version of @mattsven's answer.

Color space taken from here: 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
    }

Upvotes: 6

Kaiyuan Xu
Kaiyuan Xu

Reputation: 810

Swift 4 Version

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

Upvotes: 12

Sunkas
Sunkas

Reputation: 9590

Simpler Swift 3 extension:

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

Upvotes: 5

neoneye
neoneye

Reputation: 52181

Swift3

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
}

Upvotes: 48

abhi
abhi

Reputation: 551

Following method is find color is light or dark in Swift language based on white in color.

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
}

Following method is find color is light or dark in Swift using color components.

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
}

Upvotes: 3

Remy Vanherweghem
Remy Vanherweghem

Reputation: 3955

Using Erik Nedwidek's answer, I came up with that little snippet of code for easy inclusion.

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

Upvotes: 34

mattsven
mattsven

Reputation: 23253

My solution to this problem in a category (drawn from other answers here). Also works with grayscale colors, which at the time of writing none of the other answers do.

@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

Upvotes: 11

kakilangit
kakilangit

Reputation: 1350

If you prefer the block version:

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

Upvotes: 3

Erik Nedwidek
Erik Nedwidek

Reputation: 6184

W3C has the following: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

If you're only doing black or white text, use the color brightness calculation above. If it is below 125, use white text. If it is 125 or above, use black text.

edit 1: bias towards black text. :)

edit 2: The formula to use is ((Red value * 299) + (Green value * 587) + (Blue value * 114)) / 1000.

Upvotes: 81

Bryan Denny
Bryan Denny

Reputation: 27596

If you want to find the brightness of the color, here is some pseudo code:

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

If it is > 0.5 it is bright, and otherwise dark.

Upvotes: 0

rep_movsd
rep_movsd

Reputation: 6895

For everything that's not grayish, the RGB inverse of a color is usually highly contrasted with it. The demo just inverts the color and desaturates it (converts it to a gray).

But generating a nice soothing combination of colors is quite complicated. Look at :

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

Upvotes: 2

Related Questions