Проверьте, является ли 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");
    }
}

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

15 ответов

Решение

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

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

изменить 1: смещение в сторону черного текста.:)

изменить 2: формула для использования ((значение красного * 299) + (значение зеленого * 587) + (значение голубого * 114)) / 1000.

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

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
}

Пользуясь ответом Эрика Недуоллкка, я придумал этот небольшой фрагмент кода для легкого включения.

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

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

@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

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

Упрощенное расширение 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
    }
}

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

Цветовое пространство взято отсюда: /questions/4918094/kak-preobrazovat-tsveta-iz-odnogo-tsvetovogo-prostranstva-v-drugoe/4918103#4918103

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
    }

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

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

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

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

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

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

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

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

Использование 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 Значение чистого зеленого цвета намного выше, чем на чистом синем цвете. Это просто потому, что люди воспринимают чистый синий цвет как более темный, чем чистый зеленый.

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

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, оно светлое, а в остальном темное.

Другие вопросы по тегам