How to compare UIColors?

53,629

Solution 1

Have you tried [myColor isEqual:someOtherColor] ?

Solution 2

As zoul pointed out in the comments, isEqual: will return NO when comparing colors that are in different models/spaces (for instance #FFF with [UIColor whiteColor]). I wrote this UIColor extension that converts both colors to the same color space before comparing them:

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

Solution 3

This might be a bit too late, but CoreGraphics has an easier API to achieve this:

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

Like the documentation says:

Indicates whether two colors are equal. Two colors are equal if they have equal color spaces and numerically equal color components.

This solves a lot trouble and leaking/custom algorithms.

Solution 4

samvermette's solution translated to 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
        }
    }
}

Solution 5

This UIColor extension works fine provided that the compared colors can be converted into RGB format, which should be most of the cases.

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

At least with this extension:

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

Both comparisons would return false if compared using the native UColor.isEqual(...)

Share:
53,629
4thSpace
Author by

4thSpace

Updated on September 08, 2020

Comments

  • 4thSpace
    4thSpace over 3 years

    I'd like to check the color set for a background on a UIImageView. I've tried:

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

    but that doesn't work, even when I know the color is green, it always falls into the else part.

    Also, is there a way to output the current color in the debug console.

    p [myimage backgroundColor]
    

    and

    po [myimage backgroundColor]
    

    don't work.

  • 4thSpace
    4thSpace almost 15 years
    Thanks. What is the difference with isEqualTo? Also, do you know how to display it in the debugger?
  • zoul
    zoul over 14 years
    By the way: Be careful when comparing colors this way, because they have to be in the same color model to be considered equal. For instance, #ffffff does not equal [UIColor whiteColor].
  • borrrden
    borrrden over 11 years
    Nice, but there is a leak I think. You never release the CGColorCreate in the middle.
  • alones
    alones over 11 years
    This is a perfect solution!
  • Victor Engel
    Victor Engel over 10 years
    Can you explain what's going on with the carats? I don't think I've seen that syntax before.
  • JRG-Developer
    JRG-Developer about 10 years
    @VictorEngel, UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) ... is declaring a block. He uses it later with convertColorToRGBSpace(self). For an intro to blocks in iOS, see raywenderlich.com/9328/creating-a-diner-app-using-blocks-par‌​t-1
  • Victor Engel
    Victor Engel about 10 years
    I use blocks all the tine, but I've never seen syntax quite like this before. I guess maybe I've not used a block that returned an object. It's what's on the left side of the = that I was posting about.
  • vikingosegundo
    vikingosegundo about 10 years
    this might not work for edge cases as a pattern color. from doc If the color is in a compatible color space, the color is converted into RGB format and its components are returned to your application. If the color is not in a compatible color space, the parameters are unchanged. So you could instantiate each float with -1 and if any still has this value after calling getRed:green:blue:alpha:, you know the comparison failed. (or don't omit the returned boolean as it will tell you if it was called successfully.)
  • vikingosegundo
    vikingosegundo about 10 years
    I just checked: although it is possible to check monochrome against RGBA colors in theory, it won't work with your code, as getRed:free:blue:alpha: wont succeed on a monochrome color. So your code won't yield another result than is equal:. please see my answer how to deal with it.
  • dooleyo
    dooleyo about 10 years
    +1 for thoroughness! The above code works for my simple case, but it would appear it is no silver bullet. Good stuff.
  • pfrank
    pfrank about 10 years
    Good point Zoul, might be more useful to point to a solution not just the problem though =D
  • pfrank
    pfrank about 10 years
    color.red/blue/green aren't supported for all different types of color classes, check the docs
  • Viktor Goltvyanitsa
    Viktor Goltvyanitsa almost 10 years
    @Rudolf Adamkovic, it is Pythagorean theorem
  • mattsven
    mattsven over 9 years
    [UIColor isEqual:] does the same thing as this, does it not? Mark's answer is still the only one that checks equivalency despite color space well.
  • Echelon
    Echelon over 9 years
    Great solution, but personally I dislike the use of blocks in this way, so I modified it just to be a function. It has to accept the colorSpace as an argument of course, but it works fine and I'm sure it's more efficient if you're doing this often.
  • Echelon
    Echelon over 9 years
    This doesn't help any more than UIColor isEqual: if the colours are in different colour spaces.
  • Albert Renshaw
    Albert Renshaw almost 9 years
    Good point Pfrank, might be more useful to point to a solution not just the problem though =D
  • Pbk
    Pbk almost 9 years
    Did not always work for me when comparing a button.tintColor to the color I had set it to. Had to do with rounding. If you run into this see my answer below.
  • return true
    return true over 8 years
    But it is better than some other answers here.
  • Albert Renshaw
    Albert Renshaw almost 8 years
    As for a solution, I have not tested it yet, but perhaps getting the CGColor of the UIColor will fix the comparison issue, since that should just get right down into the values of the colors and not their types.
  • pronebird
    pronebird over 7 years
    Colors aren't the same if compared by green component only
  • Sean Vikoren
    Sean Vikoren about 7 years
    The question is regarding UIColor comparison, not CGColor comparison.
  • Kartick Vaddadi
    Kartick Vaddadi about 7 years
    Does this work while comparing colors across color spaces?
  • arunit21
    arunit21 about 7 years
    Not sure, I haven't tested it for all color spaces.
  • Nirav Gadhiya
    Nirav Gadhiya almost 7 years
    More accurate than all other solutions :) +1 from me
  • Matt Wagner
    Matt Wagner over 6 years
    This is the only answer that mentions and attempts to address potential rounding errors that can crop up while comparing colors that are set via different means; so, while it only focuses on green, the example (as the author of the answer mentions) can be generalized. As such, and since rounding was in fact the problem I was running into, I did find this answer helpful.
  • Marek H
    Marek H over 2 years
    WRONG. 2 black colors and it prints NO NSColor *color1 = [NSColor blackColor]; NSColor *color2 = [NSColor colorWithDeviceWhite:0 alpha:1]; NSLog(@"isEqual %d", CGColorEqualToColor(color1.CGColor, color2.CGColor));