mal*_*974 4 xcode objective-c uicolor cgcolorspace
我需要点击一个UIImageView...
...并将点击位置的颜色与从资产目录返回的颜色(或在代码中创建的自定义颜色)进行比较。
我在色彩空间方面遇到了很多麻烦,我的比赛总是没有。我已经阅读了一些关于 stackoverflow 的很棒的例子并尝试了它们,但我一定还是做错了什么。
我还尝试使用这样的自定义颜色(而不是资产颜色):
+ (UIColor *)themeRed {
return [UIColor colorWithRed:192.0f/255.0f green:92.0f/255.0f blue:42.0f/255.0f alpha:1];
}
Run Code Online (Sandbox Code Playgroud)
仍然没有匹配项。我的测试匹配代码如下:
-(void)tappedColorView:(UITapGestureRecognizer *)tapRecognizer {
CGPoint touchPoint = [tapRecognizer locationInView: uiiv_hs];
//NSLog(@"my color %@", [uiiv_hs colorOfPoint:touchPoint]);
UIColor *color = [uiiv_hs colorOfPoint:touchPoint];
NSLog(@"color %@",[uiiv_hs colorOfPoint:touchPoint]);
UIColor *matchcolor = [UIColor themeRed];
NSLog(@"mcolor %@",[UIColor themeRed]);
NSArray *colors = [NSArray arrayWithObjects:[UIColor colorNamed:@"Color01"],[UIColor colorNamed:@"Color02"], nil];
if ([color matchesColor:matchcolor error:nil]) {
NSLog(@"1Match!");
} else {
NSLog(@"1No Match!");
}
if ([color isEqualToColor:[UIColor themeRed]]) {
NSLog(@"2Match!");
} else {
NSLog(@"2No Match!");
}
}
Run Code Online (Sandbox Code Playgroud)
如果您不熟悉以下主题,请不要这样做。我将向您展示一种方法,一种非常简化的方法,但会有一些问题。
需要预先阅读或至少了解/熟悉它的内容。
您UIImageView可以是完全不透明的、透明的、部分不透明的、透明的,...假设有一个黄色的视图,UIImageView并且UIImageView不是不透明的,alpha 设置为 50%。你想要什么颜色?原始图像颜色?渲染颜色(与黄色混合)?
我假设与黄色混合的那个。这是获得正确颜色的代码。
目标-C:
@interface UIView(ColorAtPoint)
- (UIColor *)colorAt:(CGPoint)point;
@end
@implementation UIView(ColorAtPoint)
- (UIColor *)colorAt:(CGPoint)point {
UIView *targetOpaqueView = self;
while (!targetOpaqueView.isOpaque && targetOpaqueView.superview != nil) {
targetOpaqueView = targetOpaqueView.superview;
}
CGPoint targetPoint = [self convertPoint:point toView:targetOpaqueView];
unsigned char pixel[4] = {0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL) {
return nil;
}
CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
if (context == NULL) {
CGColorSpaceRelease(colorSpace);
return nil;
}
CGContextTranslateCTM(context, -targetPoint.x, -targetPoint.y);
[targetOpaqueView.layer renderInContext:context];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIColor *color = [UIColor colorWithRed:pixel[0]/255.0
green:pixel[1]/255.0
blue:pixel[2]/255.0
alpha:pixel[3]/255.0];
return color;
}
@end
Run Code Online (Sandbox Code Playgroud)
迅速:
extension UIView {
func color(at point: CGPoint) -> UIColor? {
var targetOpaqueView: UIView = self
// Traverse the view hierarchy to find a parent which is opaque
while !targetOpaqueView.isOpaque && targetOpaqueView.superview != nil {
targetOpaqueView = targetOpaqueView.superview!
}
// Convert the point from our view to the target one
let targetPoint: CGPoint = convert(point, to: targetOpaqueView)
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
var pixel: [UInt8] = [0, 0, 0, 0]
guard let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
return nil
}
context.translateBy(x: -targetPoint.x, y: -targetPoint.y)
// Render the target opaque view to get all the possible transparency right
targetOpaqueView.layer.render(in: context)
return UIColor(red: CGFloat(pixel[0])/255.0, green: CGFloat(pixel[1])/255.0, blue: CGFloat(pixel[2])/255.0, alpha: CGFloat(pixel[3])/255.0)
}
}
Run Code Online (Sandbox Code Playgroud)
此函数应返回 alpha 分量设置为 的颜色1.0。必须如此,1.0否则我们无法继续。为什么?想象一下,UIImageViewalpha 设置为 50% -> 我们不会遍历视图层次结构(这个targetOpaqueView舞蹈) -> 我们会得到一个颜色,alpha 分量接近0.5-> 没有任何用处,下面是什么?白色的?黑色的?橘子?
每个设备都是不同的,可以显示不同范围的颜色 - iPad、iPhone,......这还包括其他设备类型,如计算机显示器、打印机......因为我不知道你想在你的应用程序中实现什么,将其作为提醒 - 每个设备上的颜色相同,但外观不同。
这是 Display P3 配置文件和通用 sRGB 的比较。您只需在 macOS 上启动 ColorSync Utility 即可比较更多配置文件。它表明当您将一种颜色从一个空间转换为另一个空间时,颜色会有所不同。
引用颜色转换章节:
颜色转换或颜色空间转换是将颜色表示从一种颜色空间转换为另一种颜色空间。
&
几乎在每个翻译过程中,我们都必须处理这样一个事实,即不同设备的色域范围不同,这使得准确的再现变得不可能。
这是一个复杂的过程,但它取决于很多事情(色彩空间,...)。它可能涉及分色(例如 16 位 -> 8 位),这取决于您的渲染意图(相对色度、感知等)等。
非常简单的示例 - 您有一个带有红色 ( #FF0000)的图像,并为其分配了 Generic sRGB 配置文件。iPhone 将显示它 (P3),您会看到不同的颜色。更准确地说,如果您获得 RGB 分量值,它们会有所不同。
CoreGraphics.framework 提供颜色转换功能 - converted(to:intent:options:). 你必须通过:
nil)。目标-C:
@interface UIColor(ColorSpaceConversion)
- (UIColor *)convertedToColorSpace:(CGColorSpaceRef)colorSpace;
- (UIColor *)convertedToColorSpace:(CGColorSpaceRef)colorSpace inten:(CGColorRenderingIntent)intent;
@end
@implementation UIColor(ColorSpaceConversion)
- (UIColor *)convertedToColorSpace:(CGColorSpaceRef)colorSpace {
return [self convertedToColorSpace:colorSpace inten:kCGRenderingIntentDefault];
}
- (UIColor *)convertedToColorSpace:(CGColorSpaceRef)colorSpace inten:(CGColorRenderingIntent)intent {
CGColorRef converted = CGColorCreateCopyByMatchingToColorSpace(colorSpace, intent, self.CGColor, NULL);
if (converted == NULL) {
return nil;
}
UIColor *color = [[UIColor alloc] initWithCGColor:converted];
CGColorRelease(converted);
return color;
}
@end
Run Code Online (Sandbox Code Playgroud)
迅速:
extension UIColor {
func converted(toColorSpace colorSpace: CGColorSpace, intent: CGColorRenderingIntent = .defaultIntent) -> UIColor? {
guard let converted = cgColor.converted(to: colorSpace, intent: intent, options: nil) else {
return nil
}
return UIColor(cgColor: converted)
}
}
Run Code Online (Sandbox Code Playgroud)
一个例子:
#CC3333) 的分量值 (RGBA) : 0.8 0.2 0.2 1您可以通过两种方式创建颜色进行比较:
UIColor 初始化程序Xcode 资产目录允许您为不同的设备、色域指定颜色,或者您可以选择自定义内容(显示 P3、sRGB、扩展范围 sRGB,...)。
UIColor 初始值设定项允许您在(不仅仅是)中指定颜色:
请注意如何创建用于比较的颜色。RGB 分量值在各种颜色空间中有所不同。
正如您现在知道它是如何工作的那样,您可以看到其中涉及到一些数学运算,转换后的颜色无法精确匹配。我的意思是 - 两种颜色,转换为相同的颜色空间 - 你仍然不能用简单的相等运算符比较组件(RGB)。精度会因转换而丢失,即使不是 - 还记得每个计算机科学家都应该了解的关于浮点运算的知识吗?
有一种方法可以实现您想要的效果,这称为Color Difference。换句话说 - 你想计算两种颜色的距离。
目标-C:
@interface UIColor(EuclideanDistance)
- (CGFloat)euclideanDistanceTo:(UIColor *)other;
@end
@implementation UIColor(EuclideanDistance)
- (CGFloat)euclideanDistanceTo:(UIColor *)other {
CIColor *ciColor = [[CIColor alloc] initWithColor:self];
CIColor *ciColorOther = [[CIColor alloc] initWithColor:other];
if (ciColor.numberOfComponents != ciColor.numberOfComponents) {
NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException
reason:@"Colors differ in numberOfComponents"
userInfo:nil];
@throw exception;
}
if (ciColor.alpha != 1.0 || ciColorOther.alpha != 1.0) {
NSException *exception = [NSException exceptionWithName:NSInvalidArgumentException
reason:@"Transparent colors are not supported"
userInfo:nil];
@throw exception;
}
CGFloat dr = ciColorOther.red - ciColor.red;
CGFloat dg = ciColorOther.green - ciColor.green;
CGFloat db = ciColorOther.blue - ciColor.blue;
return sqrt(dr * dr + dg * dg + db * db);
}
@end
Run Code Online (Sandbox Code Playgroud)
迅速:
extension UIColor {
func euclideanDistance(to other: UIColor) -> CGFloat? {
let ciColor = CIColor(color: self)
let ciColorOther = CIColor(color: other)
guard ciColor.numberOfComponents == ciColorOther.numberOfComponents,
ciColor.alpha == 1.0, ciColorOther.alpha == 1.0 else {
return nil;
}
let dr = ciColorOther.red - ciColor.red
let dg = ciColorOther.green - ciColor.green
let db = ciColorOther.blue - ciColor.blue
return sqrt(dr * dr + dg * dg + db * db)
}
}
Run Code Online (Sandbox Code Playgroud)
我们能够计算两种颜色的欧几里德距离,让我们编写一个简单的函数来检查两种颜色是否符合一些容差:
目标-C:
@interface UIColor(Matching)
- (BOOL)matchesTo:(UIColor *)other;
- (BOOL)matchesTo:(UIColor *)other euclideanDistanceTolerance:(CGFloat)tolerance;
@end
@implementation UIColor(Matching)
- (BOOL)matchesTo:(UIColor *)other {
return [self matchesTo:other euclideanDistanceTolerance:0.01];
}
- (BOOL)matchesTo:(UIColor *)other euclideanDistanceTolerance:(CGFloat)tolerance {
CGFloat distance = [self euclideanDistanceTo:other];
return distance <= tolerance;
}
@end
Run Code Online (Sandbox Code Playgroud)
迅速:
extension UIColor {
func matches(to other: UIColor, euclideanDistanceTolerance tolerance: CGFloat = 0.01) -> Bool? {
guard let distance = euclideanDistance(to: other) else {
return nil
}
return distance <= tolerance
}
}
Run Code Online (Sandbox Code Playgroud)
#C02A2B从图像中选取红色 ( )CustomRedColor到资产目录中
#C02A2B目标-C:
@interface ViewController ()
@property (nonatomic, strong) IBOutlet UIImageView *imageView;
@property (nonatomic, strong) IBOutlet UIView *leftView;
@property (nonatomic, strong) IBOutlet UIView *rightView;
@end
@implementation ViewController
- (IBAction)handleTapGesture:(UITapGestureRecognizer *)sender {
CGPoint location = [sender locationInView:self.imageView];
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
if (colorSpace == NULL) {
return;
}
UIColor *assetsCatalogRedColor = [UIColor colorNamed:@"CustomRedColor"];
UIColor *redColor = [assetsCatalogRedColor convertedToColorSpace:colorSpace];
UIColor *tappedColor = [[self.imageView colorAt:location] convertedToColorSpace:colorSpace];
CGColorSpaceRelease(colorSpace);
self.leftView.backgroundColor = tappedColor;
self.rightView.backgroundColor = redColor;
if (redColor == nil || tappedColor == nil) {
return;
}
@try {
BOOL matches = [tappedColor matchesTo:redColor];
NSLog(@"Tapped color matches CustomRedColor: %@", matches ? @"true" : @"false");
}
@catch (NSException *exception) {
NSLog(@"Something went wrong: %@", exception);
}
}
@end
Run Code Online (Sandbox Code Playgroud)
迅速:
class ViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
@IBOutlet var leftView: UIView!
@IBOutlet var rightView: UIView!
private let assetsCatalogRedColor: UIColor = UIColor(named: "CustomRedColor")!
@IBAction
func handleTap(sender: UITapGestureRecognizer) {
let location = sender.location(in: imageView)
guard let colorSpace = CGColorSpace(name: CGColorSpace.displayP3),
let redColor = assetsCatalogRedColor.converted(toColorSpace: colorSpace, intent: .defaultIntent),
let tappedColor = imageView.color(at: location)?.converted(toColorSpace: colorSpace, intent: .defaultIntent) else {
return
}
let matches = tappedColor.matches(to: redColor) ?? false
print("Tapped color matches CustomRedColor: \(matches)")
leftView.backgroundColor = tappedColor
rightView.backgroundColor = redColor
}
}
Run Code Online (Sandbox Code Playgroud)
Tapped color matches CustomRedColor: true获得正确的颜色并不容易(定义、获取、比较、转换等)。试图在保持简单的同时指出重要的事情,但你现在应该能够做到。不要忘记正确创建颜色、转换为合适的颜色空间、选择适合您的应用程序需求的容差等。
这是包含 Objective-C 和 Swift 视图控制器实现的公共 GitHub Gist。
不需要色彩空间转换,因为[UIColor getRed:green:blue:alpha:]文档说:
如果颜色在兼容的颜色空间中,则颜色将转换为 RGB 格式并将其组件返回到您的应用程序。如果颜色不在兼容的颜色空间中,则参数不变。
&
red (green, blue) - 返回时,颜色对象的红色分量。在为 iOS 10 或更高版本链接的应用程序上,红色分量在扩展范围 sRGB 颜色空间中指定,并且可以具有任何值。0.0 和 1.0 之间的值在 sRGB 色域内。在较早版本的 iOS 上,指定的值始终介于 0.0 和 1.0 之间。
如果您使用此功能,应该为您转换颜色分量值。但是我在这个答案中保留了转换代码来演示幕后发生的事情。