Come confrontare UIColors?


127

Vorrei controllare il set di colori per uno sfondo su UIImageView. Ho provato:

if(myimage.backgroundColor == [UIColor greenColor]){
...}
else{
...}

ma non funziona, anche quando so che il colore è verde, cade sempre nella parte opposta.

Inoltre, c'è un modo per produrre il colore corrente nella console di debug.

p [myimage backgroundColor]

e

po [myimage backgroundColor]

non funziona.

Risposte:


176

Ci hai provato [myColor isEqual:someOtherColor]?


Grazie. Qual è la differenza con isEqualTo? Inoltre, sai come visualizzarlo nel debugger?
4thSpace

93
A proposito: fai attenzione quando confronti i colori in questo modo, perché devono essere nello stesso modello di colore per essere considerati uguali. Ad esempio, # ffffffnon è uguale [UIColor whiteColor].
zoul

2
Buon punto Zoul, potrebbe essere più utile indicare una soluzione non solo il problema però = D
pfrank

11
Buon punto Pfrank, potrebbe essere più utile indicare una soluzione non solo il problema però = D
Albert Renshaw,

Non ha sempre funzionato per me quando ho confrontato un button.tintColor con il colore su cui l'avevo impostato. Ha a che fare con l'arrotondamento. Se ti imbatti in questo vedi la mia risposta qui sotto.
Pbk,

75

Come sottolineato da Zoul nei commenti, isEqual:tornerà NOquando si confrontano i colori che si trovano in diversi modelli / spazi (ad esempio #FFFcon [UIColor whiteColor]). Ho scritto questa estensione UIColor che converte entrambi i colori nello stesso spazio colore prima di confrontarli:

- (BOOL)isEqualToColor:(UIColor *)otherColor {
    CGColorSpaceRef colorSpaceRGB = CGColorSpaceCreateDeviceRGB();

    UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) {
        if (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)) == kCGColorSpaceModelMonochrome) {
            const CGFloat *oldComponents = CGColorGetComponents(color.CGColor);
            CGFloat components[4] = {oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1]};
            CGColorRef colorRef = CGColorCreate( colorSpaceRGB, components );

            UIColor *color = [UIColor colorWithCGColor:colorRef];
            CGColorRelease(colorRef);
            return color;            
        } else
            return color;
    };

    UIColor *selfColor = convertColorToRGBSpace(self);
    otherColor = convertColorToRGBSpace(otherColor);
    CGColorSpaceRelease(colorSpaceRGB);

    return [selfColor isEqual:otherColor];
}

2
Bello, ma c'è una perdita credo. Non rilasciare mai CGColorCreate nel mezzo.
Borrrden,

Questa è una soluzione perfetta!
solo il

Puoi spiegare cosa sta succedendo con i carati? Non credo di aver mai visto quella sintassi prima.
Victor Engel,

@VictorEngel, UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) ...sta dichiarando a block. Lo usa più tardi con convertColorToRGBSpace(self). Per un'introduzione
JRG-Developer

1
Uso blocchi di tutto il dente, ma non ho mai visto sintassi abbastanza simile prima. Forse non ho usato un blocco che ha restituito un oggetto. È ciò che si trova sul lato sinistro del = che stavo postando.
Victor Engel,

65

Potrebbe essere un po 'troppo tardi, ma CoreGraphics ha un'API più semplice per raggiungere questo obiettivo:

CGColorEqualToColor(myColor.CGColor, [UIColor clearColor].CGColor)

Come dice la documentazione:

Indica se due colori sono uguali. Due colori sono uguali se hanno spazi colore uguali e componenti colore numericamente uguali.

Questo risolve molti problemi e perdite / algoritmi personalizzati.


5
[UIColor isEqual:]fa la stessa cosa, no? La risposta di Mark è ancora l'unica che controlla l'equivalenza nonostante lo spazio colore bene.
mattsven,

9
Questo non aiuta più che UIColor isEqual:se i colori si trovano in spazi colore diversi.
Echelon,

La domanda riguarda il confronto UIColor, non il confronto CGColor.
Sean Vikoren,

Più preciso di tutte le altre soluzioni :) +1 da parte mia
Nirav Gadhiya,

8

La soluzione di samvermette tradotta in rapido:

extension UIColor {
    func isEqualToColor(otherColor : UIColor) -> Bool {
        if self == otherColor {
            return true
        }

        let colorSpaceRGB = CGColorSpaceCreateDeviceRGB()
        let convertColorToRGBSpace : ((color : UIColor) -> UIColor?) = { (color) -> UIColor? in
            if CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)) == CGColorSpaceModel.Monochrome {
                let oldComponents = CGColorGetComponents(color.CGColor)
                let components : [CGFloat] = [ oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1] ]
                let colorRef = CGColorCreate(colorSpaceRGB, components)
                let colorOut = UIColor(CGColor: colorRef!)
                return colorOut
            }
            else {
                return color;
            }
        }

        let selfColor = convertColorToRGBSpace(color: self)
        let otherColor = convertColorToRGBSpace(color: otherColor)

        if let selfColor = selfColor, otherColor = otherColor {
            return selfColor.isEqual(otherColor)
        }
        else {
            return false
        }
    }
}

7
#import "UIColor-Expanded.h"
//https://github.com/thetaplab/uicolor-utilities

//RGB distance
CGFloat distance = sqrtf(powf((clr0.red - clr1.red), 2) + powf((clr0.green - clr1.green), 2) + powf((clr0.blue - clr1.blue), 2) );
if(distance<=minDistance){
....
}else{
...
}

@Rudolf Adamkovic, è il teorema di Pitagora
Viktor Goltvyanitsa,

6

Questa estensione UIColor funziona bene a condizione che i colori confrontati possano essere convertiti in formato RGB, che dovrebbe essere la maggior parte dei casi.

public extension UIColor {

    static func == (l: UIColor, r: UIColor) -> Bool {
        var l_red = CGFloat(0); var l_green = CGFloat(0); var l_blue = CGFloat(0); var l_alpha = CGFloat(0)
        guard l.getRed(&l_red, green: &l_green, blue: &l_blue, alpha: &l_alpha) else { return false }
        var r_red = CGFloat(0); var r_green = CGFloat(0); var r_blue = CGFloat(0); var r_alpha = CGFloat(0)
        guard r.getRed(&r_red, green: &r_green, blue: &r_blue, alpha: &r_alpha) else { return false }
        return l_red == r_red && l_green == r_green && l_blue == r_blue && l_alpha == r_alpha
    }
}

Almeno con questa estensione:

UIColor.whiteColor == UIColor(hex: "#FFFFFF") // true
UIColor.black == UIColor(red: 0, green: 0, blue: 0, alpha: 1) // true

Entrambi i confronti restituirebbero false se confrontati utilizzando l'UColor.isEqual nativo (...)


5

Ho scritto questa categoria. Se isEqual:restituisce NO, verificherà se è ancora possibile confrontare ulteriori confronti tra componenti diversi. Se possibile, vengono ancora confrontati modelli diversi.

@implementation UIColor (Matching)


-(BOOL)matchesColor:(UIColor *)color error:(NSError *__autoreleasing *)error
{
    UIColor *lhs = self;
    UIColor *rhs = color;

    if([lhs isEqual:rhs]){ // color model and values are the same
        return YES;
    }

    CGFloat red1, red2, green1, alpha1, green2, blue1, blue2, alpha2;
    BOOL lhsSuccess = [lhs getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
    BOOL rhsSuccess = [rhs getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];
    if((!lhsSuccess && rhsSuccess) || (lhsSuccess && !rhsSuccess)){ // one is RGBA, one color not.
        CGFloat r,g,b,a;
        if(!lhsSuccess){ // lhs color could be a monochrome
            const CGFloat *components = CGColorGetComponents(lhs.CGColor);
            if([lhs _colorSpaceModel] == kCGColorSpaceModelMonochrome){
                r = g = b = components[0];
                a = components[1];

                return r == red2 && g == green2 && b == blue2 && a == alpha2;
            }
        } else {  // rhs color could be a monochrome
            const CGFloat *components = CGColorGetComponents(rhs.CGColor);

            if([rhs _colorSpaceModel] == kCGColorSpaceModelMonochrome){
                r = g = b = components[0];
                a = components[1];
                return r == red1 && g == green1 && b == blue1 && a == alpha1;
            }
        }


        NSError *aError = [[NSError alloc] initWithDomain:@"UIColorComparision" code:-11111 userInfo:[self _colorComparisionErrorUserInfo]];
        *error = aError;
        return  NO;
    } else if (!lhsSuccess && !rhsSuccess){ // both not RGBA, lets try HSBA
        CGFloat hue1,saturation1,brightness1;
        CGFloat hue2,saturation2,brightness2;

        lhsSuccess = [lhs getHue:&hue1 saturation:&saturation1 brightness:&brightness1 alpha:&alpha1];
        rhsSuccess = [lhs getHue:&hue2 saturation:&saturation2 brightness:&brightness2 alpha:&alpha2];
        if((!lhsSuccess && rhsSuccess) || (lhsSuccess && !rhsSuccess)){
            NSError *aError = [[NSError alloc] initWithDomain:@"UIColorComparision" code:-11111 userInfo:[self _colorComparisionErrorUserInfo]];
            *error = aError;
            return  NO;
        } else if(!lhsSuccess && !rhsSuccess){ // both not HSBA, lets try monochrome
            CGFloat white1, white2;

            lhsSuccess = [lhs getWhite:&white1 alpha:&alpha1];
            rhsSuccess = [rhs getWhite:&white2 alpha:&alpha2];
            if((!lhsSuccess && rhsSuccess) || (lhsSuccess && !rhsSuccess)){
                NSError *aError = [[NSError alloc] initWithDomain:@"UIColorComparision" code:-11111 userInfo:[self _colorComparisionErrorUserInfo]];
                *error = aError;
                return  NO;
            } else {
                return white1 == white2 && alpha1 == alpha2;
            }

        } else {
            return hue1 == hue2 && saturation1 == saturation2 && brightness1 == brightness2 && alpha1 == alpha2;
        }

    } else {
        return (red1 == red2 && green1 == green2 && blue1 == blue2 && alpha1 == alpha2);

    }
}

-(NSDictionary *)_colorComparisionErrorUserInfo{

    NSDictionary *userInfo = @{
                               NSLocalizedDescriptionKey: NSLocalizedString(@"Comparision failed.", nil),
                               NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"The colors models are incompatible. Or the color is a pattern.", nil),

                               };
    return userInfo;
}

- (CGColorSpaceModel)_colorSpaceModel {
    return CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
}

@end

UIColor *green1 = [UIColor greenColor];
UIColor *green2 = [UIColor colorWithRed:0 green:1 blue:0 alpha:1];
UIColor *yellow = [UIColor yellowColor];
UIColor *grey1  = [UIColor colorWithWhite:2.0/3.0 alpha:1];
UIColor *grey2  = [UIColor lightGrayColor];

NSError *error1, *error2, *error3, *error4, *error5;

BOOL match1 = [green1 matchesColor:green2 error:&error1];   // YES
BOOL match2 = [green1 matchesColor:yellow error:&error2];   // NO
BOOL match3 = [green1 matchesColor:grey1 error:&error3];    // NO
BOOL match4 = [grey1 matchesColor:grey2 error:&error4];     // YES
BOOL match5 = [grey1 matchesColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"bg.png"]]
                            error:&error5];                 // NO, Error

2

Quando stai confrontando in myimage.backgroundColor == [UIColor greenColor]questo modo se non hai cambiato il backgroundColor in verde prima di quell'affermazione non funziona.

Ho avuto lo stesso problema nel mio gioco a colori e ho risolto che usando una semplice equazione di differenza nei colori RGB puoi dare un'occhiata a questo esempio di codice a colori ColorProcess da qui

è come la risposta dei vincitori

GFloat distance = sqrtf(powf((clr0.red - clr1.red), 2) + powf((clr0.green - clr1.green), 2) + powf((clr0.blue - clr1.blue), 2) );
if(distance<=minDistance){
....
}else{

}

Invece di quel codice di esempio puoi usare

include "UIColorProcess.h"

..

float distance = [UIColorProcess findDistanceBetweenTwoColor:[UIColor redColor] secondColor:[UIColor blueColor]];

e ovviamente se restituisce 0 significa che stai confrontando un colore troppo simile. l'intervallo di ritorno è simile a (0.0f - 1.5f) ..


color.red/blue/green non sono supportati per tutti i diversi tipi di classi di colori, controlla i documenti
pfrank

1

Si possono verificare alcuni strani errori di arrotondamento. Questo può essere il motivo per cui un oggetto è impostato su un colore e il colore su cui lo si imposta non corrisponde esattamente.

Ecco come l'ho risolto:

private func compareColors (c1:UIColor, c2:UIColor) -> Bool{
    // some kind of weird rounding made the colors unequal so had to compare like this

    var red:CGFloat = 0
    var green:CGFloat  = 0
    var blue:CGFloat = 0
    var alpha:CGFloat  = 0
    c1.getRed(&red, green: &green, blue: &blue, alpha: &alpha)

    var red2:CGFloat = 0
    var green2:CGFloat  = 0
    var blue2:CGFloat = 0
    var alpha2:CGFloat  = 0
    c2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2)

    return (Int(green*255) == Int(green2*255))

}

Questo codice può essere migliorato non solo confrontando 1 ma confrontando tutti i componenti. Ad esempio rosso + verde + blu + alfa == rosso2 + verde2 + blu2 + alfa2


1
I colori non sono gli stessi se confrontati solo con la componente verde
Ben Sinclair,

Questa è l'unica risposta che menziona e tenta di affrontare potenziali errori di arrotondamento che possono comparire confrontando i colori impostati con mezzi diversi; così, mentre si concentra solo sul verde, l'esempio (come l'autore della risposta menziona) può essere generalizzato. In quanto tale, e dato che l'arrotondamento era in realtà il problema che stavo incontrando, ho trovato utile questa risposta.
Matt Wagner,

1

Sto usando questa estensione che funziona per me in tutti i casi.

/***** UIColor Extension to Compare colors as string *****/
@interface UIColor (compare)
- (BOOL)compareWithColor:(UIColor *)color;
@end

@implementation UIColor(compare)
- (BOOL)compareWithColor:(UIColor *)color {
    return ([[[CIColor colorWithCGColor:self.CGColor] stringRepresentation] isEqualToString:[[CIColor colorWithCGColor:color.CGColor] stringRepresentation]]);
}
@end
/**** End ****/

La speranza aiuta qualcuno.

Nota: #ffffffequivale [UIColor whiteColor]a questa estensione


Funziona mentre si confrontano i colori attraverso gli spazi colore?
Vaddadi Kartick,

Non sono sicuro, non l'ho testato per tutti gli spazi colore.
Arunit21,

0

Che dire:

+(BOOL)color:(UIColor *)color1 matchesColor:(UIColor *)color2
{
    CGFloat red1, red2, green1, green2, blue1, blue2, alpha1, alpha2;
    [color1 getRed:&red1 green:&green1 blue:&blue1 alpha:&alpha1];
    [color2 getRed:&red2 green:&green2 blue:&blue2 alpha:&alpha2];

    return (red1 == red2 && green1 == green2 && blue1 == blue2 && alpha1 == alpha2);
}

questo potrebbe non funzionare per i casi limite come colore del motivo. da doc Se il colore si trova in uno spazio colore compatibile, il colore viene convertito in formato RGB e i suoi componenti vengono restituiti all'applicazione. Se il colore non si trova in uno spazio colore compatibile, i parametri rimangono invariati. Quindi potresti istanziare ogni float con -1e se qualcuno ha ancora questo valore dopo aver chiamato getRed:green:blue:alpha:, sai che il confronto non è riuscito. (o non omettere il booleano restituito in quanto ti dirà se è stato chiamato correttamente.)
vikingosegundo

1
Ho appena controllato: anche se in teoria è possibile verificare il monocromio rispetto ai colori RGBA, non funzionerà con il tuo codice, poiché getRed:free:blue:alpha:non riuscirà su un colore monocromatico. Quindi il tuo codice non produrrà un risultato diverso da is equal:. per favore, vedi la mia risposta su come gestirla.
vikingosegundo,

+1 per completezza! Il codice sopra funziona per il mio semplice caso, ma sembrerebbe che non sia un proiettile d'argento. Roba buona.
dooleyo

0

Ecco un'estensione per passare a RGC Space Color in Swift:

extension UIColor {

func convertColorToRGBSpaceColor() -> UIColor {
    let colorSpaceRGB = CGColorSpaceCreateDeviceRGB()
    let oldComponents = CGColorGetComponents(self.CGColor)
    let components = [oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1]]
    let colorRef = CGColorCreate(colorSpaceRGB, components)
    let convertedColor = UIColor(CGColor: colorRef!)
    return convertedColor
}

}


0

Estensione a UIColor, utilizzando le funzionalità di Swift 2.2. Tuttavia, poiché i valori RGBA vengono confrontati e questi sono CGFloat, gli errori di arrotondamento possono rendere i colori non restituiti uguali se non sono esattamente uguali (ad esempio, non sono stati originariamente creati utilizzando le stesse proprietà esatte nell'init (...)!).

/**
 Extracts the RGBA values of the colors and check if the are the same.
 */

public func isEqualToColorRGBA(color : UIColor) -> Bool {
    //local type used for holding converted color values
    typealias colorType = (red : CGFloat, green : CGFloat, blue : CGFloat, alpha : CGFloat)
    var myColor         : colorType = (0,0,0,0)
    var otherColor      : colorType = (0,0,0,0)
    //getRed returns true if color could be converted so if one of them failed we assume that colors are not equal
    guard getRed(&myColor.red, green: &myColor.green, blue: &myColor.blue, alpha: &myColor.alpha) &&
        color.getRed(&otherColor.red, green: &otherColor.green, blue: &otherColor.blue, alpha: &otherColor.alpha)
        else {
            return false
    }
    log.debug("\(myColor) = \(otherColor)")
    //as of Swift 2.2 (Xcode 7.3.1), tuples up to arity 6 can be compared with == so this works nicely
    return myColor == otherColor
}

0

Estensione UIColor

- (CGFloat)accuracyCompareWith:(UIColor *)color {
    CIColor *c1 = [[CIColor alloc] initWithColor:self];
    CIColor *c2 = [[CIColor alloc] initWithColor:color];

    BOOL hasAlpha = c1.numberOfComponents == 4 && c2.numberOfComponents == 4;
    NSInteger numberOfComponents = hasAlpha ? 4 : 3;

    CGFloat colorMax = 1.0;
    CGFloat p = colorMax / 100.0;

    CGFloat redP = fabs(c1.red / p - c2.red / p);
    CGFloat greenP = fabs(c1.green / p - c2.green / p);
    CGFloat blueP = fabs(c1.blue / p - c2.blue / p);
    CGFloat alphaP = 0;

    if (hasAlpha)
        alphaP = fabs(c1.alpha / p - c2.alpha / p);

    return (redP + greenP + blueP + alphaP) / (CGFloat)numberOfComponents;
}

0

Ho convertito la risposta di raf a Swift 4 (molte modifiche CGColorall'API), rimosso lo scostamento forzato e ridotto il rientro grazie all'uso generoso di guard:

@extension UIColor {
    func isEqualToColor(otherColor: UIColor) -> Bool {
        if self == otherColor {
            return true
        }
        let colorSpaceRGB = CGColorSpaceCreateDeviceRGB()
        let convertColorToRGBSpace: ((UIColor) -> UIColor?) = { (color) -> UIColor? in
            guard color.cgColor.colorSpace?.model == .monochrome else {
                return color
            }
            guard let oldComponents = color.cgColor.components else {
                return nil
            }
            let newComponents = [oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1]]
            guard let colorRef = CGColor(colorSpace: colorSpaceRGB, components: newComponents) else {
                    return nil
            }
            return UIColor(cgColor: colorRef)
        } 

        guard let selfColor = convertColorToRGBSpace(self), 
              let otherColor = convertColorToRGBSpace(otherColor) else {
            return false
        }
        return selfColor.isEqual(otherColor)
    }
}

0

Perché non aggiungere l'estensione con protocollo equatable? Questa risposta sta usando la soluzione di Nicolas Miari. Quindi, se ti piace questa risposta, ti preghiamo di gradire la sua risposta (seconda dall'alto)

Il commento di Zoul: Fai attenzione quando confronti i colori in questo modo, perché devono essere dello stesso modello di colore per essere considerati uguali. Ad esempio, #ffffff non è uguale a [UIColor whiteColor]

static func == (lhs: UIColor, rhs: UIColor) -> Bool {

    let colorSpaceRGB = CGColorSpaceCreateDeviceRGB()
    let convertColorToRGBSpace: ((UIColor) -> UIColor?) = { (color) -> UIColor? in
        guard color.cgColor.colorSpace?.model == .monochrome else {
            return color
        }
        guard let oldComponents = color.cgColor.components else {
            return nil
        }
        let newComponents = [oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1]]
        guard let colorRef = CGColor(colorSpace: colorSpaceRGB, components: newComponents) else {
            return nil
        }
        return UIColor(cgColor: colorRef)
    }

    guard let selfColor = convertColorToRGBSpace(lhs),
        let otherColor = convertColorToRGBSpace(rhs) else {
            return false
    }
    return selfColor.isEqual(otherColor)
}

0

Mentre @ di samvermette risposta è molto buono, ho trovato che a volte può portare a falsi negativi quando si confrontano diversi tipi di colore (nel mio caso UIDeviceRGBColora UICachedDeviceWhiteColor). L'ho risolto creando esplicitamente anche il colore nell'altro:

- (BOOL)isEqualToColor:(UIColor *)otherColor
{
    if (self == otherColor)
        return YES;

    CGColorSpaceRef colorSpaceRGB = CGColorSpaceCreateDeviceRGB();

    UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color)
    {
        if (CGColorSpaceGetModel(CGColorGetColorSpace(color.CGColor)) == kCGColorSpaceModelMonochrome)
        {
            const CGFloat *oldComponents = CGColorGetComponents(color.CGColor);
            CGFloat components[4] = {oldComponents[0], oldComponents[0], oldComponents[0], oldComponents[1]};
            CGColorRef colorRef = CGColorCreate(colorSpaceRGB, components);
            UIColor *color = [UIColor colorWithCGColor:colorRef];
            CGColorRelease(colorRef);
            return color;
        }
        else
        {
            const CGFloat *oldComponents = CGColorGetComponents(color.CGColor);
            CGFloat components[4] = {oldComponents[0], oldComponents[1], oldComponents[2], oldComponents[3]};
            CGColorRef colorRef = CGColorCreate(colorSpaceRGB, components);
            UIColor *color = [UIColor colorWithCGColor:colorRef];
            CGColorRelease(colorRef);
            return color;
        }
    };

    UIColor *selfColor = convertColorToRGBSpace(self);
    otherColor = convertColorToRGBSpace(otherColor);
    CGColorSpaceRelease(colorSpaceRGB);

    return [selfColor isEqual:otherColor];
}

0

Devi usare

BOOL equalColors = CGColorEqualToColor(uiColor1.CGColor, uiColor2.CGColor));

Documentazione qui .


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.