Перевірте, чи 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.
Глен Хоуз

Відповіді:


75

W3C має таке: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Якщо ви робите лише чорно-білий текст, використовуйте вищевказаний розрахунок яскравості кольору. Якщо вона нижче 125, використовуйте білий текст. Якщо це 125 або вище, використовуйте чорний текст.

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

редагувати 2: Формула, яку слід використовувати ((Червоне значення * 299) + (Зелене значення * 587) + (Синє значення * 114)) / 1000.


чи знаєте ви, як отримати значення червоного, зеленого та синього кольору?
Andre Andre

Ви можете використовувати NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);для отримання масиву кольорових компонентів, включаючи альфа. Документи не вказують, в якому порядку вони перебувають, але я припускаю, що це буде червоний, зелений, синій, альфа. Крім того, з документів "розмір масиву на один більший, ніж кількість компонентів кольорового простору для кольору". - не сказано, чому ...
Jasarien

Це дивно ... використовуючи: NSArray * компоненти = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "мої кольорові компоненти% f% f% f% f", компоненти [0], компоненти [1], компоненти [2], компоненти [3]); просто щоб побачити, чи можу я отримати значення, здається, що змінився лише індекс 0, інші залишаться такими ж, незалежно від того, який колір я вибираю. Будь-яка ідея чому?
Андре

Зрозумів. Ви не можете використовувати NSArray. Використовуйте це замість: const CGFloat * компоненти = CGColorGetComponents (newColor.CGColor); Також порядок такий: червоний - це компоненти [0]; зелений - компоненти [1]; синій - компоненти [2]; альфа - компоненти [3];
Andre Andre

Ах, мій поганий. Я думав, що це повернуло CFArrayRef, а не масив C. Вибачте!
Джасаріен

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 7.12.18


6
Працює чудово. З Swift 2.0 - у мене з’явилася помилка компілятора для обчислення яскравості: "Вираз був занадто складним, щоб його вирішити в розумний час; розгляньте розбиття виразу на окремі підвислови". Я виправив це, додавши тип: "нехай яскравість = (((компоненти [0] * 299.0) як CGFloat) + ((компоненти [1] * 587.0) як CGFloat) + ((компоненти [2] * 114.0)) як CGFloat ) / (1000,0 як CGFloat) "
GK100

1
У мене була така ж проблема із Swift 2.1. Я вирішив цю проблему, просто створивши змінні для компонентів замість доступу до них components[0]. let componentColorX: CGFloat = components[1]
Ось

1
Xcode підказує мені яскравість = "Вираз був занадто складним, щоб його можна було вирішити в розумний час; розглянути питання про розбиття виразу на різні
підвислови

daidai, просто спростіть вираз. Поділ в кінці непотрібний. Нам не потрібно працювати в діапазоні 0 - 1. Не діліться на 1000, а просто перевіряйте, чи не перевищує це значення, а більше 500.
aronspring

32

Користуючись відповіддю Еріка Недролинека, я придумав той маленький фрагмент коду для легкого включення.

- (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], він повертає темністьScore = 149.685. Чи можете ви пояснити, чому це відбувається?
DivineDesert

3
Цей код не працюватиме з кольорами, які не є RGB, такими як UIColor whiteColor, grayColor, blackColor.
rmaddy

@rmaddy чому це? чи чому кольори білого кольору, сірого кольору та чорного кольору RGB не є?
матцвен

1
@rmaddy Як я можу програмно визначити різницю між кольорами в кольоровому просторі RGB проти відтінків сірого?
матцвен

1
@maddy Nevermind! Дякую за допомогу - 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

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 для обліку цього.
Сункас

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 Andre

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, як білий. Тому я маю спочатку перевірити кольоровий простірModel. Це те, що я придумав, що в кінцевому підсумку став швидкою версією відповіді @ 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

Для перетворення в кольоровий простір HSB використовується UIColor :

- (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-колір не працює з цим кодом.
rmaddy

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
Не можна зважувати кожен із кольорів однаково. Наші очі мають упередженість щодо синього та меншою мірою червоного.
Ерік Недширолик

@ErikNedwidek: Досить справедливо, питання просто попросив яскравість кольору :)
Брайан Денні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.