How to compare 2 UIColor objects that are instances of kCGColorSpaceModelRGB and UIExtendedSRGBColorSpace in logs?

Now I'm really confused. Here's how to instantiate a variable:

Utils.redColor = UIColor(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue)/255.0, alpha: alpha) 

And here I list the attributes of the attribute text to skip the color if it is equal to Utils.redColor:

  text.enumerateAttributes(in: NSRange(0..<text.length), options: []) { (attributes, range, _) -> Void in for (attribute, object) in attributes { if let textColor = object as? UIColor{ NSLog("textColor = \(textColor) red = \(Utils.redColor!)") if (!textColor.isEqual(Utils.redColor!)){ //I need to repaint any textColor other than red text.setAttributes(textAttributes , range: range) } } 

So, as you can see in this textColor code there is a UIColor object, but the log says:

 textColor = kCGColorSpaceModelRGB 0.666667 0.172549 0.172549 1 red = UIExtendedSRGBColorSpace 0.666667 0.172549 0.172549 1 

What are two exact colors, but are instances of two different classes. This is completely confusing, since both of them are objects of the UIColor class!

This comparison never works, although it works well in Swift2

How to fix it and why does this problem ever occur?

+5
source share
1 answer

Welcome to the wild and woolen world of wide color and color management.

Your two colors are not equal, for isEqual (or Swift == , which goes through isEqual for the ObjC classes that have it), because they have different color spaces. (They are not different classes, the first element in UIColor.description is the identifier of the color space or where the color space does not have a name, the model for the color space - that is, whether it is based on RGB, CMYK, grayscale, etc.)

Without the color space to determine their color, the four values ​​of the color component do not have a reliable value, so isEqual uses both the component values ​​and the color space to check for equality.

Beyond Color Spaces (skip down for solutions)

Your color created using UIColor init(red:green:blue:alpha:) uses the "Extended sRGB" color space. This color space is designed to support wide color displays (for example, the P3 color display on the iPhone 7, iPad Pro 9.7 ", iMac at the end of 2015, the MacBook Pro at the end of 2016, and maybe something else will come next), but are compatible with components with sRGB color space used on other devices.

For example, sRGB 1.0, 0.0, 0.0 is the β€œred” that you probably use most often ... but if you create a color in the P3 color space with RGB 1.0, 0.0, 0.0 , you will get a lot more. If you have an application in which you need to support sRGB and P3 displays and work directly with color components, this can be confusing. Thus, the Extended sRGB space allows the same component values ​​to mean the same thing, but also allows you to specify colors outside the sRGB gamut, using values ​​outside the range 0.0 - 1.0 . For example, the 1.093, -0.227, -0.15 Display P3 can display is expressed in Extended sRGB as (approximately) 1.093, -0.227, -0.15 .

Like [docs for this initializer note, for applications related to iOS 10 or later SDK, init(red:green:blue:alpha:) creates color in the extended sRGB color space, but for older applications (even if they work on iOS 10), it creates color in a device-defined RGB space (which can usually be considered equivalent to sRGB).

Work with different color spaces

So, either your color replacement code or some code creates colors in your attribute string, you need to know the color spaces. There are several possible ways to deal with this; Choose the one that best suits you:

  • Make sure that your line creation code and color swap code use the same device-independent color space. UIColor does not provide many utilities for working with color spaces, so you can use Display P3 (on iOS 10 and above) or CGColor down to CGColor :

     let sRGB = CGColorSpace(name: CGColorSpace.sRGB)! let cgDarkRed = CGColor(colorSpace: sRGB, components: [0.666667, 0.172549, 0.172549, 1])! let darkRed = UIColor(cgColor: cgDarkRed) // example creating attributed string... let attrString = NSAttributedString(string: "red", attributes: [NSForegroundColorAttributeName : darkRed]) // example processing text... let redAttributes = [NSForegroundColorAttributeName: darkRed] text.enumerateAttributes(in: NSRange(0..<attrString.length)) { (attributes, range, stop) in for (_, textColor) in attributes where (textColor as? UIColor) != darkRed { text.setAttributes(redAttributes , range: range) } } 
  • If you cannot control the input colors, convert them to the same color space before comparison. Here is the UIColor extension for this:

     extension UIColor { func isEqualWithConversion(_ color: UIColor) -> Bool { guard let space = self.cgColor.colorSpace else { return false } guard let converted = color.cgColor.converted(to: space, intent: .absoluteColorimetric, options: nil) else { return false } return self.cgColor == converted } } 

    (Then you can simply use this function instead of == or isEqual in your text processing.)

  • Just get the values ​​of the original color components and compare them directly based on the assumption that you know that the color spaces are compatible for both. Relatively fragile, so I recommend against this option.

+11
source

Source: https://habr.com/ru/post/1261510/


All Articles