如何比较UIColor?

131

我想检查UIImageView背景的颜色集。 我已经尝试过:

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

但是那样不起作用,即使我知道颜色是绿色,它总是进入else部分。

此外,是否有一种方法可以在调试控制台中输出当前颜色。

p [myimage backgroundColor]

po [myimage backgroundColor]

无法工作。

20个回答

177

你尝试过 [myColor isEqual:someOtherColor] 吗?


谢谢。isEqualTo有什么区别?您知道如何在调试器中显示它吗? - 4thSpace
101
顺便提一下:在比较颜色时要小心,因为它们必须处于同一种颜色模型才能被认为是相等的。例如,#ffffff 不等于 [UIColor whiteColor]。 - zoul
3
好观点Zoul,不过指出问题的同时指向解决方案可能更有用 =D - pfrank
12
好观点Pfrank,可能更有用的是指出解决方案而不仅仅是问题 =D - Albert Renshaw
当我将button.tintColor与我设置的颜色进行比较时,它并不总是有效。这与四舍五入有关。如果你遇到了这个问题,请查看下面的答案。 - Pbk
至于解决方案,我尚未测试过,但也许获取UIColor的CGColor将修复比较问题,因为它应该直接进入颜色的值而不是它们的类型。 - Albert Renshaw

78

正如zoul在评论中指出的,isEqual:方法在比较不同模型或空间的颜色时(例如将#FFF[UIColor whiteColor]进行比较),会返回NO。我编写了这个UIColor扩展,它会将两种颜色转换为相同的颜色空间后再进行比较:

- (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
不错,但我认为有一个泄漏。您从未在中间释放CGColorCreate。 - borrrden
这是一个完美的解决方案! - alones
你能解释一下这些尖角符号在干什么吗?我之前好像没见过这种语法。 - Victor Engel
@VictorEngel,UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) ... 声明了一个 block。他稍后使用 convertColorToRGBSpace(self)。有关 iOS 中块的介绍,请参见 http://www.raywenderlich.com/9328/creating-a-diner-app-using-blocks-part-1 - JRG-Developer
1
我经常使用块,但我从未见过这样的语法。我猜可能是我没有使用返回对象的块。我发帖的是=左侧的内容。 - Victor Engel
很棒的解决方案,但我个人不喜欢以这种方式使用块,所以我将其修改为函数。当然,它必须接受colorSpace作为参数,但它运行良好,如果您经常这样做,我相信它更有效率。 - Echelon

65

可能有些晚了,但CoreGraphics有一个更简单的API可以实现这个:

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

就像文档所述:

指示两种颜色是否相等。 如果它们具有相等的颜色空间和数值上相等的颜色组件,则两种颜色是相等的。

这解决了很多麻烦,以及泄漏/定制算法。


5
[UIColor isEqual:] 和这个做的事情一样,对吗?尽管颜色空间不同,但 Mark 的回答仍然是唯一检查等价性的答案。 - mattsven
10
如果颜色位于不同的颜色空间中,那么这与“UIColor isEqual:”一样没有帮助。 - Echelon
问题涉及UIColor比较,而不是CGColor比较。 - Sean Vikoren
比其他所有解决方案更准确 :) 我给你点赞 - Nirav Gadhiya
错误。有两个黑色颜色,但输出的是NO。 NSColor *color1 = [NSColor blackColor]; NSColor *color2 = [NSColor colorWithDeviceWhite:0 alpha:1]; NSLog(@"isEqual %d", CGColorEqualToColor(color1.CGColor, color2.CGColor)); - Marek H

8

samvermette的解决方案翻译成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

只要被比较的颜色可以转换为RGB格式,这个UIColor扩展就能够正常工作,而这应该是大多数情况。

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

至少使用这个扩展程序:

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

如果使用原生的 UColor.isEqual(...) 进行比较,两个对象都会返回 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,这是勾股定理。 - Viktor Goltvyanitsa

5
我写了这个类别。如果isEqual:返回NO,它将测试是否仍然可以匹配不同组件的进一步比较。如果可能,不同的模型仍然会被比较。
@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
当你像这样比较myimage.backgroundColor == [UIColor greenColor]时,如果在该语句之前未将backgroundColor更改为绿色,则不起作用。
我在我的彩色游戏中遇到了同样的问题,通过在RGB颜色中使用简单的差分方程,我解决了这个问题,你可以快速查看这个短代码示例ColorProcess,它来自这里
就像Victor的答案一样。
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{}

你可以使用以下代码示例代替该代码:

include "UIColorProcess.h"

..

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

当然,如果返回值为0,则表示您正在比较相似的颜色。返回范围大致为(0.0f - 1.5f)。


注:此处的“返回范围”可能指的是函数的返回值范围。

颜色类的所有不同类型都不支持color.red/blue/green,查看文档。 - pfrank

1
一些奇怪的舍入误差可能会发生。这可能是对象设置为某种颜色,但您设置的颜色与实际颜色不完全匹配的原因。
这是我解决它的方法:
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))

}

这段代码可以通过比较所有组件而不仅仅是比较1来进行改进。例如,红色+绿色+蓝色+透明度 == 红色2+绿色2+蓝色2+透明度2。

1
仅通过绿色分量进行比较时,颜色并不相同。 - pronebird
这是唯一一个提到并试图解决可能出现的四舍五入误差的答案,这种误差可能在比较通过不同方式设置的颜色时出现。因此,尽管它只关注绿色,但示例(正如答案的作者所指出的)可以概括。因此,由于四舍五入确实是我遇到的问题,我发现这个回答很有帮助。 - Matt Wagner

1

我正在使用这个扩展,在所有情况下都能正常工作。

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

我希望能够帮助到某些人。

注意:这个扩展中的#ffffff等同于[UIColor whiteColor]


在跨颜色空间比较颜色时,这是否有效? - Kartick Vaddadi
不确定,我还没有测试所有的颜色空间。 - arunit21

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接