Controlla se UIColor è scuro o luminoso?


107

Ho bisogno di determinare se un UIColor selezionato (scelto dall'utente) è scuro o luminoso, così posso cambiare il colore di una riga di testo che si trova sopra quel colore, per una migliore leggibilità.

Ecco un esempio in Flash / Actionscript (con demo): http://web.archive.org/web/20100102024448/http://theflashblog.com/?p=173

qualche idea?

Salute, Andre

AGGIORNARE

Grazie ai suggerimenti di tutti, ecco il codice funzionante:

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

Grazie ancora :)


Sembra che alcuni colori non abbiano 3 componenti, come UIColor.black.
Glenn Howes

Risposte:


75

W3C ha quanto segue: http://www.w3.org/WAI/ER/WD-AERT/#color-contrast

Se stai solo scrivendo testo in bianco o nero, usa il calcolo della luminosità del colore sopra. Se è inferiore a 125, utilizza il testo bianco. Se è 125 o superiore, usa il testo nero.

modifica 1: preferenza verso il testo nero. :)

modifica 2: la formula da utilizzare è ((valore rosso * 299) + (valore verde * 587) + (valore blu * 114)) / 1000.


sai come ottenere i valori di rosso, verde e blu di un colore?
Andre

È possibile utilizzare NSArray *components = (NSArray *)CGColorGetComponents([UIColor CGColor]);per ottenere un array dei componenti del colore, incluso l'alfa. I documenti non specificano in quale ordine si trovano ma presumo che sarebbe rosso, verde, blu, alfa. Inoltre, dalla documentazione, "la dimensione della matrice è una in più rispetto al numero di componenti dello spazio colore per il colore". - non dice perché ...
Jasarien

È strano ... utilizzando: NSArray * components = (NSArray *) CGColorGetComponents (newColor.CGColor); NSLog (@ "componenti del mio colore% f% f% f% f", componenti [0], componenti [1], componenti [2], componenti [3]); solo per vedere se riesco a ottenere i valori, sembra che cambi solo l'indice 0, gli altri rimarranno gli stessi, indipendentemente dal colore che scelgo. Qualche idea sul perché?
Andre il

Fatto. Non puoi usare NSArray. Usa questo invece: const CGFloat * components = CGColorGetComponents (newColor.CGColor); Inoltre, l'ordine è: il rosso è i componenti [0]; il verde è componenti [1]; il blu è componenti [2]; alfa è componenti [3];
Andre il

Ah, colpa mia. Ho pensato che restituisse un CFArrayRef, non un array C. Scusa!
Jasarien

44

Ecco un'estensione Swift (3) per eseguire questo controllo.

Questa estensione funziona con i colori in scala di grigi. Tuttavia, se si creano tutti i colori con l'inizializzatore RGB e non si utilizzano i colori incorporati come UIColor.blacke UIColor.white, è possibile rimuovere i controlli aggiuntivi.

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

test:

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

Nota: aggiornato a Swift 3 12/7/18


6
Funziona alla grande. Con Swift 2.0 - ho ricevuto un errore del compilatore per il calcolo della luminosità: "L'espressione era troppo complessa per essere risolta in un tempo ragionevole; considera la possibilità di suddividere l'espressione in sottoespressioni distinte". Ho risolto il problema aggiungendo il tipo: "lascia la luminosità = (((components [0] * 299.0) come CGFloat) + ((components [1] * 587.0) come CGFloat) + ((components [2] * 114.0)) come CGFloat ) / (1000.0 come CGFloat) "
GK100

1
Ho avuto lo stesso problema con Swift 2.1. Ho risolto questo problema semplicemente creando variabili per i componenti invece di accedervi con components[0]. In questo modo:let componentColorX: CGFloat = components[1]
petritz

1
Xcode mi dice che la luminosità = "L'espressione era troppo complessa per essere risolta in un tempo ragionevole; considera la possibilità di suddividere l'espressione in sottoespressioni distinte"
daidai

daidai, semplifica solo l'espressione. La divisione alla fine non è necessaria. Non abbiamo bisogno di lavorare nell'intervallo 0 - 1. Non dividere per 1000, quindi controlla semplicemente se il valore è maggiore di 500.
primavera del

32

Usando la risposta di Erik Nedwidek, ho trovato quel piccolo frammento di codice per una facile inclusione.

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

Ho usato questo codice, ha funzionato perfettamente, ma non so quando ho impostato [UIColor blackColor], restituisce darknessScore = 149.685. Puoi spiegare perché sta succedendo?
DivineDesert

3
Questo codice non funzionerà con colori non RGB come UIColor whiteColor, grayColor, blackColor.
rmaddy

@rmaddy perché è così? o perché i colori whiteColor, grayColor e blackColor non sono RGB?
mattsven

1
@rmaddy Come posso stabilire a livello di programmazione la differenza tra i colori nello spazio colore RGB rispetto alla scala di grigi?
mattsven

1
@maddy Nevermind! Grazie per l'aiuto - 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
}

Per me questo è il migliore. Puoi persino impostare la gamma di bianco nel segno di spunta in base alle tue esigenze ed è semplicissimo e nativo. Grazie.
Andreas777

9

Versione 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
Probabilmente non funzionerà per i colori di sistema predefiniti come UIColor.white poiché avrà solo 2 componenti, non è una rappresentazione RGB.
Skrew

7

La mia soluzione a questo problema in una categoria (tratta da altre risposte qui). Funziona anche con i colori in scala di grigi, cosa che al momento della scrittura di nessuna delle altre risposte funziona.

@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

buona gestione dei colori non RGB - funziona come un fascino.
hatunike

5

Estensione Swift 3 più semplice:

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
Probabilmente non funzionerà per i colori di sistema predefiniti come UIColor.white poiché avrà solo 2 componenti, non è una rappresentazione RGB.
Skrew

Vero! È possibile convertire il colore in stringa esadecimale e quindi di nuovo in UIColor per tenerne conto.
Sunkas

3

Se preferisci la versione in blocco:

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

Per tutto ciò che non è grigiastro, l'inverso RGB di un colore è solitamente molto contrastato con esso. La demo inverte semplicemente il colore e lo desatura (lo converte in grigio).

Ma generare una bella combinazione rilassante di colori è piuttosto complicato. Guarda a :

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


1
Se solo ci fosse una versione per iPhone di quello: D
Andre

Quindi se ho ottenuto i valori RGB di un colore (cosa che non so come fare) e ho creato un nuovo colore con l'inverso di quei valori, dovrebbe funzionare a un livello molto semplice?
Andre

2

Il seguente metodo è trovare il colore è chiaro o scuro nel linguaggio Swift basato sul colore bianco.

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
}

Il seguente metodo è trovare il colore è chiaro o scuro in Swift utilizzando i componenti del colore.

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

Per me che uso solo CGColorGetComponents non ha funzionato, ottengo 2 componenti per UIColors come il bianco. Quindi devo prima controllare lo spazio coloreModel. Questo è ciò che mi è venuto in mente e che alla fine è stata la versione rapida della risposta di @ mattsven.

Spazio colore preso da qui: 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

UIColor ha il seguente metodo per convertire nello spazio colore HSB :

- (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 5Versione aggiunta :

var white: CGFloat = 0.0
color.getWhite(&white, alpha: nil)
return white >= .85 // Don't use white background

1
Funziona solo se UIColorè un colore in scala di grigi. Un colore RGB casuale non funzionerà con questo codice.
rmaddy

0

Se vuoi trovare la luminosità del colore, ecco uno pseudo codice:

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

Se è> 0,5 è luminoso, altrimenti scuro.


2
Non puoi pesare allo stesso modo ciascuno dei colori. I nostri occhi hanno una predilezione contro il blu e in misura minore il rosso.
Erik Nedwidek

@ErikNedwidek: Giusto, la domanda chiedeva semplicemente la luminosità di un colore :)
Bryan Denny
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.