iOS在两种颜色之间的点处查找颜色

我有一个问题:我需要能够采用两种颜色并从中制作出“虚拟渐变”。 然后我需要能够在这一行的任何一点找到颜色。 我目前的做法是这样的:

if (fahrenheit  kTopThreshold) { return [UIColor colorWithRed:kTopR/255.0f green:kTopG/255.0f blue:kTopB/255.0f alpha:1]; } double rDiff = kTopR - kBottomR; double gDiff = kTopG - kBottomG; double bDiff = kTopB - kBottomB; double tempDiff = kTopThreshold - kBottomThreshold; double rValue; double gValue; double bValue; rValue = kBottomR + ((rDiff/tempDiff) * fahrenheit); gValue = kBottomG + ((gDiff/tempDiff) * fahrenheit); bValue = kBottomB + ((bDiff/tempDiff) * fahrenheit); return [UIColor colorWithRed:rValue/255.0f green:gValue/255.0f blue:bValue/255.0f alpha:1]; 

变量:

  • fahrenheit是一个传递给我的函数的变量,它是我想要找到颜色的虚拟行上的数字。
  • kTopRkTopBkTopG是梯度一端的RGB值。 他们的kBottom同行也是如此。
  • kBottomThresholdkTopThreshold是我的渐变的端点。

这是我的问题: fahrenheit超过梯度的任何一端时,渐变似乎“跳”到不同的值。

我在这里列出了一个托管在我的S3服务器上的示例项目。

你真的需要下载项目并在模拟器/设备上试一试,看看我的意思 (除非你疯狂聪明,只能看看代码)

问题是你没有从kBottomThreshold减去kBottomThreshold

但是,让我们简化。

首先,我们要将输入温度映射到[0 … 1]范围内的参数t 。 然后,我们想要将t映射到[ kBottomRkTopR ]范围内的输出,还要映射到[ kBottomRkTopR ]范围内的输出,还要映射到范围[ kBottomBkTopB ]。

 UIColor *colorForDegreesFahrenheit(double fahrenheit) { double t = (fahrenheit - kBottomThreshold) / (kTopThreshold - kBottomThreshold); // Clamp t to the range [0 ... 1]. t = MAX(0.0, MIN(t, 1.0)); double r = kBottomR + t * (kTopR - kBottomR); double g = kBottomG + t * (kTopG - kBottomG); double b = kBottomB + t * (kTopB - kBottomB); return [UIColor colorWithRed:r/255 green:g/255 blue:b/255 alpha:1]; } 

Swift – 3.0 && 4.0

 extension UIColor { func toColor(_ color: UIColor, percentage: CGFloat) -> UIColor { let percentage = max(min(percentage, 100), 0) / 100 switch percentage { case 0: return self case 1: return color default: var (r1, g1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) var (r2, g2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) guard self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) else { return self } guard color.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) else { return self } return UIColor(red: CGFloat(r1 + (r2 - r1) * percentage), green: CGFloat(g1 + (g2 - g1) * percentage), blue: CGFloat(b1 + (b2 - b1) * percentage), alpha: CGFloat(a1 + (a2 - a1) * percentage)) } } } 

用法:-

 let colorRed = UIColor.red let colorBlue = UIColor.blue let colorOutput = colorRed.toColor(colorBlue, percentage: 50) 

结果

在此处输入图像描述

如果您的渐变比2色渐变更复杂,您可以考虑将CGGradientRef绘制到临时CGImageRef中,并直接从图像缓冲区读取RGBA值。

这是我用5个渐变色块和颜色做的事情:

  CGFloat tmpImagewidth = 1000.0f; // Make this bigger or smaller if you need more or less resolution (number of different colors). CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // create a gradient CGFloat locations[] = { 0.0, 0.35, 0.55, 0.8, 1.0 }; NSArray *colors = @[(__bridge id) [UIColor redColor].CGColor, (__bridge id) [UIColor greenColor].CGColor, (__bridge id) [UIColor blueColor].CGColor, (__bridge id) [UIColor yellowColor].CGColor, (__bridge id) [UIColor redColor].CGColor, ]; CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations); CGPoint startPoint = CGPointMake(0, 0); CGPoint endPoint = CGPointMake(tmpImagewidth, 0); // create a bitmap context to draw the gradient to, 1 pixel high. CGContextRef context = CGBitmapContextCreate(NULL, tmpImagewidth, 1, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast); // draw the gradient into it CGContextAddRect(context, CGRectMake(0, 0, tmpImagewidth, 1)); CGContextClip(context); CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); // Get our RGB bytes into a buffer with a couple of intermediate steps... // CGImageRef -> CFDataRef -> byte array CGImageRef cgImage = CGBitmapContextCreateImage(context); CGDataProviderRef provider = CGImageGetDataProvider(cgImage); CFDataRef pixelData = CGDataProviderCopyData(provider); // cleanup: CGGradientRelease(gradient); CGColorSpaceRelease(colorSpace); CGImageRelease(cgImage); CGContextRelease(context); const UInt8* data = CFDataGetBytePtr(pixelData); // we got all the data we need. // bytes in the data buffer are a succession of RGBA bytes // For instance, the color of the point 27% in our gradient is: CGFloat x = tmpImagewidth * .27; int pixelIndex = (int)x * 4; // 4 bytes per color UIColor *color = [UIColor colorWithRed:data[pixelIndex + 0]/255.0f green:data[pixelIndex + 1]/255.0f blue:data[pixelIndex + 2]/255.0f alpha:data[pixelIndex + 3]/255.0f]; // done fetching color data, finally release the buffer CGDataProviderRelease(provider); 

我并不是说这比上面的答案中的“数学方式”更好,当然存在生成临时图像的内存和CPU税。 然而,这样做的好处是,无论您需要多少梯度停止,代码复杂性都保持不变…

我想扩展/修改@Sebastien Windal的答案,因为他的答案对我的用例非常有用,但是可以通过直接从上下文获取pixelData来进一步改进(参见swift中的UIImage / CGImage中的像素数据获取数据) )。

我在swift中实现了他的答案,因此变化也是迅速写的。

在绘制渐变之前,只需将Int Array传递给上下文。 在绘制上下文时,将使用pixelData填充数组。

 let dataSize = tmpImagewidth * 1 * 4 var pixelData = [UInt8](repeating: 0, count: Int(dataSize)) let context = CGContext(data: &pixelData, width: Int(tmpImagewidth), height: 1, bitsPerComponent: 8, bytesPerRow: 4 * Int(tmpImagewidth), space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) 

然后我们可以跳过创建图像,从那里读取pixelData。

Interesting Posts