¿Cómo comparar UIColors?


127

Me gustaría verificar el conjunto de colores para un fondo en un UIImageView. He intentado:

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

pero eso no funciona, incluso cuando sé que el color es verde, siempre cae en la otra parte.

Además, ¿hay alguna forma de generar el color actual en la consola de depuración?

p [myimage backgroundColor]

y

po [myimage backgroundColor]

no trabajes

Respuestas:


176

¿Lo has intentado [myColor isEqual:someOtherColor]?


Gracias. ¿Cuál es la diferencia con isEqualTo? Además, ¿sabe cómo mostrarlo en el depurador?
4thSpace

93
Por cierto: tenga cuidado al comparar colores de esta manera, ya que tienen que estar en el mismo modelo de color para ser considerados iguales. Por ejemplo, # ffffffno es igual [UIColor whiteColor].
zoul

2
Buen punto Zoul, podría ser más útil señalar una solución, no solo el problema, aunque = D
pfrank

11
Buen punto Pfrank, podría ser más útil señalar una solución, no solo el problema, aunque = D
Albert Renshaw

No siempre funcionó para mí al comparar un button.tintColor con el color en el que lo había configurado. Tenía que ver con el redondeo. Si te encuentras con esto, mira mi respuesta a continuación.
Pbk

75

Como señaló Zoul en los comentarios, isEqual:volverá NOal comparar colores que están en diferentes modelos / espacios (por ejemplo, #FFFcon [UIColor whiteColor]). Escribí esta extensión UIColor que convierte ambos colores en el mismo espacio de color antes de compararlos:

- (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
Bien, pero creo que hay una fuga. Nunca sueltas el CGColorCreate en el medio.
Borrrden

¡Esta es una solución perfecta!
solo el

¿Puedes explicar qué está pasando con los quilates? No creo haber visto esa sintaxis antes.
Victor Engel

@VictorEngel, UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) ...está declarando a block. Lo usa más tarde con convertColorToRGBSpace(self). Para una introducción a los bloques en iOS, vea raywenderlich.com/9328/creating-a-diner-app-using-blocks-part-1
JRG-Developer

1
Uso bloques todos los dientes, pero nunca había visto una sintaxis como esta antes. Supongo que tal vez no he usado un bloque que devolvió un objeto. Es lo que está en el lado izquierdo del = sobre el que estaba publicando.
Victor Engel

65

Esto puede ser demasiado tarde, pero CoreGraphics tiene una API más fácil para lograr esto:

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

Como dice la documentación:

Indica si dos colores son iguales. Dos colores son iguales si tienen espacios de color iguales y componentes de color numéricamente iguales.

Esto resuelve muchos problemas y fugas / algoritmos personalizados.


55
[UIColor isEqual:]hace lo mismo que esto, ¿no? La respuesta de Mark sigue siendo la única que verifica la equivalencia a pesar del espacio de color.
mattsven

9
Esto no ayuda más que UIColor isEqual:si los colores están en diferentes espacios de color.
Echelon

La pregunta se refiere a la comparación UIColor, no a la comparación CGColor.
Sean Vikoren

Más preciso que todas las otras soluciones :) +1 de mi parte
Nirav Gadhiya

8

La solución de samvermette traducida a swift:

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, es el teorema de Pitágoras
Viktor Goltvyanitsa

6

Esta extensión UIColor funciona bien siempre que los colores comparados se puedan convertir a formato RGB, que debería ser la mayoría de los casos.

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

Al menos con esta extensión:

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

Ambas comparaciones devolverían falso si se compara con el UColor.isEqual nativo (...)


5

Escribí esta categoría. Si isEqual:devuelve NO, probará si la comparación adicional de diferentes componentes aún podría coincidir. Si es posible, todavía se comparan diferentes modelos.

@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

Cuando se compara de myimage.backgroundColor == [UIColor greenColor]esta manera, si no ha cambiado el color de fondo a verde antes de esa declaración, no funciona.

Tuve el mismo problema en mi juego de colores y resolví que al usar una ecuación de diferencia simple en colores RGB, puedes echar un vistazo a ese breve ejemplo de código ColorProcess desde aquí

es como la respuesta de los vencedores

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{

}

En lugar de ese ejemplo de código, puede usar

include "UIColorProcess.h"

..

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

y, por supuesto, si devuelve 0, eso significa que está comparando un color demasiado similar. el rango de retorno es algo así como (0.0f - 1.5f) ..


color.red/blue/green no son compatibles con todos los diferentes tipos de clases de color, consulte los documentos
pfrank

1

Se pueden producir algunos errores de redondeo extraños. Esa puede ser la razón por la que un objeto se establece en un color y el color con el que lo configuró no coincide exactamente.

Así es como lo resolví:

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

}

Este código se puede mejorar no solo comparando 1 sino comparando todos los componentes. Por ejemplo, rojo + verde + azul + alfa == rojo2 + verde2 + azul2 + alfa2


1
Los colores no son los mismos si se comparan solo con el componente verde
Ben Sinclair

Esta es la única respuesta que menciona e intenta abordar los posibles errores de redondeo que pueden surgir al comparar los colores que se establecen a través de diferentes medios; entonces, si bien solo se enfoca en el verde, el ejemplo (como el autor de la respuesta menciona) puede generalizarse. Como tal, y dado que el redondeo era el problema con el que me encontraba, encontré esta respuesta útil.
Matt Wagner

1

Estoy usando esta extensión que funciona para mí en todos los casos.

/***** 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 esperanza ayuda a alguien.

Nota: #ffffffes igual [UIColor whiteColor]por esta extensión


¿Funciona esto mientras se comparan colores en espacios de color?
Vaddadi Kartick

No estoy seguro, no lo he probado para todos los espacios de color.
arunit21

0

Qué pasa:

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

Esto podría no funcionar para casos extremos como un color de patrón. from doc Si el color está en un espacio de color compatible, el color se convierte al formato RGB y sus componentes se devuelven a su aplicación. Si el color no está en un espacio de color compatible, los parámetros no cambian. Por lo tanto, puede crear una instancia de cada flotante -1y, si alguno aún tiene este valor después de llamar getRed:green:blue:alpha:, sabe que la comparación falló. (o no omita el booleano devuelto, ya que le dirá si se llamó con éxito).
vikingosegundo

1
Acabo de comprobar: aunque es posible verificar el monocromo con los colores RGBA en teoría, no funcionará con su código, ya que getRed:free:blue:alpha:no tendrá éxito en un color monocromático. Por lo tanto, su código no producirá otro resultado que is equal:. por favor vea mi respuesta cómo lidiar con eso.
vikingosegundo

+1 por minuciosidad! El código anterior funciona para mi caso simple, pero parece que no es una bala de plata. Buen material.
dooleyo

0

Aquí hay una extensión para cambiar al RGC Space Color en 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

Extensión a UIColor, utilizando las funciones de Swift 2.2. Sin embargo, tenga en cuenta que debido a que los valores RGBA se comparan, y estos son CGFloat, los errores de redondeo pueden hacer que los colores no se devuelvan como iguales si no son exactamente iguales (por ejemplo, no se crearon originalmente usando exactamente las mismas propiedades en el 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

Extensión 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

He convertido la respuesta de raf a Swift 4 (muchos cambios en la CGColorAPI), eliminé el desenvolvimiento forzado y disminuí la sangría gracias al uso generoso de 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

¿Por qué no agregar la extensión con protocolo equitativo? Esta respuesta está usando la solución de Nicolas Miari. Por lo tanto, si le gusta esta respuesta, le invitamos a que le guste su respuesta (segundo desde arriba)

El comentario de Zoul: Tenga cuidado al comparar colores de esta manera, ya que tienen que estar en el mismo modelo de color para ser considerados iguales. Por ejemplo, #ffffff no es igual 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

Si bien la respuesta de @ samvermette es muy buena, descubrí que a veces puede dar lugar a falsos negativos al comparar diferentes tipos de color (en mi caso, UIDeviceRGBColorcon UICachedDeviceWhiteColor). También lo arreglé creando explícitamente el color en el 'else':

- (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

Tienes que usar

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

Documentación aquí .


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.