如何比较UIColors?


127

我想检查UIImageView上背景的颜色设置。我试过了:

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

但这是行不通的,即使我知道颜色是绿色,它也总是落在其他部分。

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

p [myimage backgroundColor]

po [myimage backgroundColor]

不工作

Answers:


176

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


谢谢。与isEqualTo有什么区别?另外,您知道如何在调试器中显示它吗?
4thSpace,2009年

93
顺便说一句:以这种方式比较颜色时要小心,因为它们必须处于同一颜色模型中才能被视为相等。例如,#ffffff不等于[UIColor whiteColor]
zoul

2
好的观点,祖尔,也许更有用的是指出一个解决方案,不仅是问题,虽然= D
pfrank

11
好点Pfrank,可能会更有用地指出一个解决方案,不仅是问题,而是= D
Albert Renshaw 2015年

比较button.tintColor和我设置的颜色时,它并不总是对我有用。不得不舍入。如果遇到此问题,请在下面查看我的答案。
Pbk

75

正如zoul在评论中指出的那样,当比较不同模型/空间(例如与中)中的颜色时,isEqual:将返回。我编写了这个UIColor扩展,可以在比较它们之前将两种颜色转换为相同的颜色空间:NO#FFF[UIColor whiteColor]

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

这是一个完美的解决方案!
2012年

您能解释一下克拉发生了什么吗?我认为我以前没有看过这种语法。
维克托·恩格尔

@VictorEngel UIColor *(^convertColorToRGBSpace)(UIColor*) = ^(UIColor *color) ...正在声明block。他稍后将其用于convertColorToRGBSpace(self)。有关iOS中的块的介绍,请参阅raywenderlich.com/9328/creating-a-diner-app-using-blocks-part-1
JRG-Developer

1
我使用了所有的分支,但以前从未见过像这样的语法。我想也许我没有使用返回对象的块。这就是我发布的=左侧的内容。
维克多·恩格尔

65

这可能为时已晚,但是CoreGraphics有一个更简单的API可以实现此目的:

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

就像文档中说的那样:

指示两种颜色是否相等。如果两种颜色具有相等的色彩空间和数字上相等的颜色分量,则它们是相等的。

这解决了很多麻烦和泄漏/自定义算法。


5
[UIColor isEqual:]与此相同,不是吗?尽管色彩空间很好,但马克的答案仍然是唯一检查等效性的答案。
mattsven 2014年

9
UIColor isEqual:如果颜色在不同的颜色空间中,则无济于事。
梯队2015年

问题是关于UIColor比较,而不是CGColor比较。
肖恩·维柯伦

比所有其他解决方案更准确:)从我+1
Nirav Gadhiya

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


6

如果比较的颜色可以转换为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。


5

我写了这个类别。如果isEqual:返回否,它将测试不同组件的进一步比较是否仍然匹配。如果可能,仍将比较不同的模型。

@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

像胜利者一样回答

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

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,还可以通过比较所有组件来改进此代码。例如red + green + blue + alpha == red2 + green2 + blue2 + alpha2


1
如果相比仅绿色分量的颜色是不一样的
本·辛克莱尔

这是唯一提及并试图解决在比较通过不同方式设置的颜色时可能出现的舍入误差的答案。因此,虽然它只关注绿色,但是可以概括该示例(正如答案的作者所提到的)。因此,由于四舍五入实际上是我遇到的问题,因此我确实找到了这个答案。
马特·瓦格纳

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]


在跨颜色空间比较颜色时,这有用吗?
Vaddadi Kartick '17

不确定,我尚未针对所有色彩空间进行过测试。
arunit21年

1

其他解决方案无法正常工作。这是在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

关于什么:

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

这可能不适用于作为图案颜色的边缘情况。from doc 如果颜色在兼容的颜色空间中,则该颜色将转换为RGB格式,并且其成分将返回到您的应用程序。如果颜色不在兼容的颜色空间中,则参数不变。因此,您可以使用实例化每个float,-1如果在调用之后仍然有该值getRed:green:blue:alpha:,则知道比较失败。(或者不要忽略返回的布尔值,因为它会告诉您是否成功调用了它。)
vikingosegundo 2014年

1
我刚刚检查了一下:尽管从理论上讲可以检查单色与RGBA颜色,但是它不能与您的代码一起使用,因为getRed:free:blue:alpha:单色不能成功。因此,您的代码不会产生另一个结果is equal:。请参阅我的答案如何处理。
vikingosegundo 2014年

+1为彻底!上面的代码适用于我的简单情况,但看起来并不是灵丹妙药。好东西。
dooleyo 2014年

0

这是在Swift中切换到RGC空间颜色的扩展:

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

使用Swift 2.2功能扩展到UIColor。但是请注意,由于是比较RGBA值,并且它们是CGFloat,因此舍入错误会使颜色不完全相同(例如,它们最初不是使用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

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

我已经将rafSwift 4 的答案CGColorAPI 中的许多更改)转换为,由于慷慨地使用,因此消除了力解开并减少了缩进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的解决方案。因此,如果您喜欢这个答案,欢迎您喜欢他的答案(排名第二)

Zoul的评论:以这种方式比较颜色时要小心,因为它们必须处于同一颜色模型中才能被视为相等。例如,#ffffff不等于[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

@samvermette的回答非常好,但我发现在比较不同的颜色类型(在我的情况下UIDeviceRGBColorUICachedDeviceWhiteColor)时,有时会导致假阴性。我也通过在“ 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];
}


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.