Убедитесь, что UIColor темный или яркий?

Мне нужно определить, является ли выбранный 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");
    }
}

Еще раз спасибо :)

Ответ 1

W3C имеет следующее: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Если вы делаете только черный или белый текст, используйте расчет яркости цвета выше. Если он меньше 125, используйте белый текст. Если он равен 125 или выше, используйте черный текст.

edit 1: смещение к черному тексту.:)

edit 2: Используемая формула ((Красное значение * 299) + (Зеленое значение * 587) + (Синее значение * 114))/1000.

Ответ 2

Используя ответ 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];
}

Ответ 3

Вот расширение 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

Ответ 4

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
}

Ответ 5

Мое решение этой проблемы в категории (взято из других ответов здесь). Также работает с оттенками серого, которые на момент написания ни одного из других ответов не делали.

@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

Ответ 6

Версия 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)
    }
}

Ответ 7

Расширение Simpler 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
    }
}

Ответ 8

Если вы предпочитаете версию блока:

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

Ответ 9

Для всего, что не серовато, RGB, обратный к цвету, обычно сильно контрастирует с ним. Демо просто инвертирует цвет и обесцветяет его (преобразует его в серый).

Но создание приятного успокаивающего сочетания цветов довольно сложно. Посмотрите:

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

Ответ 10

Следующим методом является поиск цвета светлым или темным на языке 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
}

Ответ 11

Для меня использование только CGColorGetComponents не сработало, я получаю 2 компонента для UIColors, таких как белый. Поэтому сначала нужно проверить цветовое пространствоModel. Это то, что я придумал, и это стало быстрой версией ответа @mattsven.

Цветовое пространство взято здесь: fooobar.com/questions/9994/...

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
    }

Ответ 12

UIColor имеет следующий метод для преобразования в цветовое пространство HSB:

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

Ответ 13

Если вы хотите найти яркость цвета, вот несколько псевдокодов:

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, оно яркое и в противном случае темное.

Ответ 14

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

Ответ 15

Использование HandyUIKit делает это без проблем, например:

import HandyUIKit    

UIColor.black.hlca.luminance     // => 0.0
UIColor.white.hlca.luminance     // => 1.0
UIColor.lightGray.hlca.luminance // => 0.70
UIColor.darkGray.hlca.luminance  // => 0.36
UIColor.red.hlca.luminance       // => 0.53
UIColor.green.hlca.luminance     // => 0.87
UIColor.blue.hlca.luminance      // => 0.32

Просто используя .hlca.luminance на любом экземпляре UIColor, вы получите правильное значение воспринимаемой легкости. Большинство других подходов не учитывают тот факт, что люди воспринимают разные цвета (даже когда насыщенность и яркость одинаковы) на разных уровнях яркости. Значения luminance учитывают это.

Прочитайте это замечательное сообщение в блоге или запись в Википедии Цветовое пространство LAB для дальнейшего объяснения. Они также объяснят, почему цветная система HSB с ее значением brightness составляет только половину решения.

Также как подсказка: обратите внимание, что значение luminance цвета чистый зеленый намного выше, чем на чисто-синем цвете. Это просто потому, что люди воспринимают чистый синий цвет как более темный цвет, чем чистый зеленый.