web-dev-qa-db-fra.com

Comment comparer UIColors?

Je voudrais vérifier la couleur définie pour un fond sur un UIImageView. J'ai essayé:

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

mais cela ne fonctionne pas, même si je sais que la couleur est verte, elle tombe toujours dans la partie "autres".

De même, existe-t-il un moyen de sortir la couleur actuelle dans la console de débogage?.

p [myimage backgroundColor]

et

po [myimage backgroundColor]

ne fonctionne pas.

118
4thSpace

Avez-vous essayé [myColor isEqual:someOtherColor]?

171
Steven Canfield

Comme l'a souligné Zoul dans les commentaires, isEqual: renverra NO lors de la comparaison de couleurs se trouvant dans différents modèles/espaces (par exemple #FFF avec [UIColor whiteColor]). J'ai écrit cette extension UIColor qui convertit les deux couleurs dans le même espace colorimétrique avant de les comparer:

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

Cela peut être un peu trop tard, mais CoreGraphics a une API plus facile pour y parvenir:

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

Comme le dit la documentation: 

Indique si deux couleurs sont égales . Deux couleurs sont égales si elles ont des espaces de couleur égaux et des composantes de couleur numériquement égales.

Cela résout beaucoup de problèmes et des algorithmes volants/personnalisés.

60
DZenBot

la solution de samvermette traduite en 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
        }
    }
}
8
raf
#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{
...
}
7
Viktor Goltvyanitsa

J'ai écrit cette catégorie. Si isEqual: renvoie NO, il vérifiera si une comparaison ultérieure de différents composants peut toujours correspondre. Si possible, différents modèles sont encore comparés.

@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
5
vikingosegundo

Cette extension UIColor fonctionne correctement à condition que les couleurs comparées puissent être converties au format RVB, ce qui devrait être la plupart du temps. 

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

Au moins avec cette extension:

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

Les deux comparaisons renvoient false si elles sont comparées avec UColor.isEqual (...)

5
boherna

Lorsque vous comparez myimage.backgroundColor == [UIColor greenColor] comme ceci, si vous n'avez pas changé la couleur d'arrière-plan en vert avant cette instruction, cela ne fonctionne pas.

J'ai eu le même problème dans mon jeu de couleurs et j'ai résolu qu'en utilisant une simple équation aux différences dans les couleurs RVB, vous pouvez rapidement jeter un coup d'œil à l'exemple de code court ColorProcess de ici

c'est comme les vainqueurs répondent

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

Au lieu de cet exemple de code, vous pouvez utiliser 

include "UIColorProcess.h"

..

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

et bien sûr, si elle renvoie 0, cela signifie que vous comparez une couleur trop similaire. la plage de retour est quelque chose comme (0.0f - 1.5f) ..

2
mert

J'utilise cette extension qui fonctionne pour moi dans tous les cas. 

/***** 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 ****/

Hope aide quelqu'un.

Remarque: #ffffff est égal à [UIColor whiteColor] par cette extension

1
arunit21

J'ai converti la réponse de raf en Swift 4 (de nombreux changements dans l'API CGColor), le dépliage forcé et le retrait indenté réduit grâce à l'utilisation généreuse 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
Nicolas Miari

Pourquoi ne pas ajouter l'extension avec le protocole equatable? Cette réponse utilise la solution de Nicolas Miari. Donc, si vous aimez cette réponse, n'hésitez pas à aimer sa réponse (deuxième en partant du haut)

Le commentaire de Zoul: Soyez prudent lorsque vous comparez les couleurs de cette manière, car elles doivent être dans le même modèle de couleur pour être considérées comme égales. Par exemple, #ffffff n'est pas égal à [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
Illya Krit

Extension 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
uranpro

Bien que la réponse de @ samvermette soit très bonne, j'ai constaté qu'elle pouvait parfois conduire à de faux négatifs lors de la comparaison de types de couleurs différents (dans mon cas, UIDeviceRGBColor à UICachedDeviceWhiteColor). Je l'ai corrigé en créant explicitement la couleur dans le `else ', aussi:

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

Qu'en est-il de:

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

Des erreurs d'arrondis étranges peuvent survenir. Cela peut être la raison pour laquelle un objet est défini sur une couleur et la couleur que vous définissez ne pas correspondre exactement.

Voici comment je l'ai résolu: 

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

}

Ce code peut être amélioré non seulement en comparant 1, mais en comparant tous les composants. Par exemple, rouge + vert + bleu + alpha == rouge2 + vert2 + bleu2 + alpha2

0
Pbk

Voici une extension pour basculer vers le RGC Space Color dans 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
RyanPliske

Les autres solutions ne fonctionnaient pas correctement. Voici une approche qui fonctionne dans Swift:

import CoreGraphics

extension UIColor {

    /**
     Checks for visual equality of two colors regardless of their color space.
     - parameter    color:  The other color for comparison.
     - returns:     A boolean representing whether the colors are visually the same color.
     */
    public func isVisuallyEqualTo(_ color: UIColor) -> Bool {
        return rgbaComponents == color.rgbaComponents
    }

    public var rgbaComponents: [CGFloat] {
        var data = [CUnsignedChar](repeating: 0, count: 4)
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: &data,
                                width: 1, height: 1,
                                bitsPerComponent: 8, bytesPerRow: 4,
                                space: colorSpace,
                                bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)
        context?.setFillColor(cgColor)
        context?.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
        return data.map { CGFloat($0) / 255.0 }
    }

}
0
Steve Uffelman

Extension à UIColor, utilisant les fonctionnalités de Swift 2.2 . Notez cependant que, étant donné que les valeurs RGBA sont comparées, il s’agit de CGFloat, des erreurs d’arrondi peuvent faire en sorte que les couleurs ne soient pas restituées de n’a pas été créé à l’origine en utilisant exactement les mêmes propriétés dans 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
HixField