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
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];
}
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
}
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
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
}
}
UIColor has the following method to convert to HSB color space:
- (BOOL)getHue:(CGFloat *)hue saturation:(CGFloat *)saturation brightness:(CGFloat *)brightness alpha:(CGFloat *)alpha;
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.