Überprüfen Sie, ob UIColor dunkel oder hell ist.

107

Ich muss feststellen, ob eine ausgewählte UIColor (vom Benutzer ausgewählt) dunkel oder hell ist, damit ich die Farbe einer Textzeile ändern kann, die über dieser Farbe liegt, um die Lesbarkeit zu verbessern.

Hier ist ein Beispiel in Flash / Actionscript (mit Demo): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

Irgendwelche Gedanken?

Prost, Andre

AKTUALISIEREN

Dank aller Vorschläge ist hier der Arbeitscode:

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

Noch einmal vielen Dank :)

Andre
quelle

Antworten:

75

W3C hat Folgendes: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Wenn Sie nur schwarzen oder weißen Text erstellen, verwenden Sie die obige Berechnung der Farbhelligkeit. Wenn es unter 125 liegt, verwenden Sie weißen Text. Wenn es 125 oder höher ist, verwenden Sie schwarzen Text.

edit 1: Neigung zu schwarzem Text. :) :)

Bearbeiten 2: Die zu verwendende Formel lautet ((roter Wert * 299) + (grüner Wert * 587) + (blauer Wert * 114)) / 1000.

Erik Nedwidek
quelle
Wissen Sie, wie Sie die Rot-, Grün- und Blauwerte einer Farbe erhalten?
Andre
Sie können verwenden NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);, um ein Array der Farbkomponenten einschließlich des Alphas abzurufen. Die Dokumente geben nicht an, in welcher Reihenfolge sie sich befinden, aber ich gehe davon aus, dass es sich um Rot, Grün, Blau und Alpha handelt. In den Dokumenten heißt es außerdem: "Die Größe des Arrays ist eins mehr als die Anzahl der Komponenten des Farbraums für die Farbe." - es sagt nicht warum ...
Jasarien
Das ist seltsam ... mit: NSArray * components = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "meine Farbkomponenten% f% f% f% f", Komponenten [0], Komponenten [1], Komponenten [2], Komponenten [3]); Nur um zu sehen, ob ich die Werte erhalten kann, scheint sich nur der 0-Index zu ändern, die anderen bleiben gleich, unabhängig davon, welche Farbe ich wähle. Irgendeine Idee warum?
Andre
Verstanden. Sie können NSArray nicht verwenden. Verwenden Sie stattdessen Folgendes: const CGFloat * components = CGColorGetComponents (newColor.CGColor); Außerdem lautet die Reihenfolge: Rot ist Komponenten [0]; Grün ist Komponenten [1]; Blau ist Komponenten [2]; Alpha ist Komponenten [3];
Andre
Ah, mein schlechtes. Ich dachte, es würde ein CFArrayRef zurückgeben, kein C-Array. Es tut uns leid!
Jasarien
44

Hier ist eine Swift (3) -Erweiterung, um diese Prüfung durchzuführen.

Diese Erweiterung funktioniert mit Graustufenfarben. Wenn Sie jedoch alle Ihre Farben mit dem RGB - initializer schaffen und nicht die eingebauten Farben wie die Verwendung UIColor.blackund UIColor.white, dann können Sie möglicherweise die zusätzlichen Prüfungen entfernen.

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

Hinweis: Aktualisiert auf Swift 3 07.12.18

Josh-Fuggle
quelle
6
Funktioniert super. Mit Swift 2.0 - Ich habe einen Compilerfehler für die Helligkeitsberechnung erhalten: "Der Ausdruck war zu komplex, um in angemessener Zeit gelöst zu werden. Zerlegen Sie den Ausdruck in verschiedene Unterausdrücke." Ich habe es behoben, indem ich den Typ hinzugefügt habe: "Helligkeit = (((Komponenten [0] * 299.0) als CGFloat) + ((Komponenten [1] * 587.0) als CGFloat) + ((Komponenten [2] * 114.0)) als CGFloat ) / (1000.0 als CGFloat)
GK100
1
Ich hatte das gleiche Problem mit Swift 2.1. Ich habe dieses Problem gelöst, indem ich einfach Variablen für die Komponenten erstellt habe, anstatt mit ihnen darauf zuzugreifen components[0]. Wie so:let componentColorX: CGFloat = components[1]
Petritz
1
Xcode sagt mir die Helligkeit = "Ausdruck war zu komplex, um in angemessener Zeit gelöst zu werden; erwägen Sie, den Ausdruck in verschiedene Unterausdrücke
aufzuteilen
Daidai, vereinfache einfach den Ausdruck. Die Aufteilung am Ende ist nicht erforderlich. Wir müssen nicht im Bereich von 0 bis 1 arbeiten. Teilen Sie nicht durch 1000, sondern prüfen Sie stattdessen, ob der Wert größer als 500 ist.
Feder
32

Mit der Antwort von Erik Nedwidek habe ich mir diesen kleinen Codeausschnitt ausgedacht, um ihn einfach einbinden zu können.

- (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];
}
Remy Vanherweghem
quelle
Ich habe diesen Code verwendet, habe perfekt funktioniert, weiß aber nicht, wann ich [UIColor blackColor] eingestellt habe. Er gibt darkScore = 149.685 zurück. Können Sie erklären, warum dies geschieht?
DivineDesert
3
Dieser Code funktioniert nicht mit Nicht-RGB-Farben wie z UIColor whiteColor, grayColor, blackColor.
rmaddy
@rmaddy warum ist das so? oder warum sind nicht whiteColor, greyColor und blackColor RGB-Farben?
Mattsven
1
@rmaddy Wie kann ich programmgesteuert den Unterschied zwischen Farben im RGB-Farbraum und Graustufen erkennen?
Mattsven
1
@maddy Nevermind! Vielen Dank für die Hilfe - stackoverflow.com/questions/1099569/…
mattsven
31

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
}
neoneye
quelle
Für mich ist dieser der beste. Sie können sogar einen Weißbereich im Scheck einstellen, der Ihren Anforderungen entspricht und super einfach und nativ ist. Vielen Dank.
Andreas777
9

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)
    }
}
Kaiyuan Xu
quelle
1
Dies funktioniert wahrscheinlich nicht für Standardsystemfarben wie UIColor.white, da es nur 2 Komponenten enthält und keine RGB-Darstellung ist.
Skrew
7

Meine Lösung für dieses Problem in einer Kategorie (aus anderen Antworten hier). Funktioniert auch mit Graustufenfarben, was zum Zeitpunkt des Schreibens keine der anderen Antworten tut.

@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
mattsven
quelle
Gute Handhabung von Nicht-RGB-Farben - funktioniert wie ein Zauber.
Hatunike
5

Einfachere Swift 3-Erweiterung:

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
    }
}
Sunkas
quelle
2
Dies funktioniert wahrscheinlich nicht für Standardsystemfarben wie UIColor.white, da es nur 2 Komponenten enthält und keine RGB-Darstellung ist.
Skrew
Wahr! Sie können die Farbe in Hex-String und dann zurück in UIColor konvertieren, um dies zu berücksichtigen.
Sunkas
3

Wenn Sie die Blockversion bevorzugen:

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;
};
Kakilangit
quelle
2

Für alles, was nicht grau ist, wird die RGB-Umkehrung einer Farbe normalerweise stark kontrastiert. Die Demo invertiert nur die Farbe und entsättigt sie (konvertiert sie in ein Grau).

Das Erzeugen einer schönen beruhigenden Farbkombination ist jedoch ziemlich kompliziert. Ansehen :

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

rep_movsd
quelle
1
Wenn es nur eine iPhone-Version davon gäbe: D
Andre
Wenn ich also die RGB-Werte einer Farbe habe (was ich nicht kann) und eine neue Farbe mit der Umkehrung dieser Werte erstellt habe, sollte das auf einer sehr einfachen Ebene funktionieren?
Andre
2

Die folgende Methode lautet: Finden Sie, dass die Farbe in der Swift-Sprache hell oder dunkel ist, basierend auf der weißen Farbe.

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
}

Die folgende Methode besteht darin, mithilfe von Farbkomponenten zu ermitteln, ob die Farbe in Swift hell oder dunkel ist.

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
}
abhi
quelle
2

Für mich hat es nicht funktioniert, nur CGColorGetComponents zu verwenden. Ich bekomme 2 Komponenten für UIColors wie Weiß. Also muss ich zuerst das color spaceModel überprüfen. Dies ist, was ich mir ausgedacht habe, was die schnelle Version von @ mattsvens Antwort war.

Farbraum von hier genommen: 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
    }
Lucho
quelle
2

UIColor verfügt über die folgende Methode zum Konvertieren in den HSB-Farbraum :

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

Hinzugefügte Swift 5Version:

var white: CGFloat = 0.0
color.getWhite(&white, alpha: nil)
return white >= .85 // Don't use white background
Mike Glukhov
quelle
1
Dies funktioniert nur, wenn UIColores sich um eine Graustufenfarbe handelt. Eine zufällige RGB-Farbe funktioniert mit diesem Code nicht.
rmaddy
0

Wenn Sie die Helligkeit der Farbe ermitteln möchten, finden Sie hier einen Pseudocode:

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

Wenn es> 0,5 ist, ist es hell und ansonsten dunkel.

Bryan Denny
quelle
2
Sie können nicht jede Farbe gleich gewichten. Unsere Augen neigen zu Blau und in geringerem Maße zu Rot.
Erik Nedwidek
@ErikNedwidek: Fair genug, die Frage stellte einfach nach der Helligkeit einer Farbe :)
Bryan Denny