74

我知道可以通过在矩形图像上绘制 CGContextFillRect 并设置混合模式来着色。但是,我不知道如何对透明图像(例如图标)进行着色。这必须是可能的,因为 SDK 会在标签栏上自行完成。有人能提供一个片段吗?

更新:

自从我最初询问以来,已经针对这个问题提出了很多很好的建议。请务必通读所有答案,找出最适合您的答案。

更新(2015 年 4 月 30 日):

使用 iOS 7.0,我现在可以执行以下操作,这将满足我最初问题的需求。但是,如果您有更复杂的案例,请查看所有答案。

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
4

13 回答 13

165

更新:这是使用下面代码的 Swift UIColor 扩展的要点


如果您有灰度图像并希望白色成为着色颜色,那么您kCGBlendModeMultiply可以这样做。使用这种方法,您不能使高光比您的着色颜色更亮。

相反,如果您有一个非灰度图像或者您有应该保留的高光和阴影,那么混合模式就是要走kCGBlendModeColor路。由于保留了图像的亮度,白色将保持白色,黑色将保持黑色。此模式仅用于着色 - 它与 Photoshop 的Color图层混合模式相同(免责声明:结果可能会略有不同)。

请注意,着色 alpha 像素在 iOS 和 Photoshop 中都无法正常工作 - 半透明黑色像素不会保持黑色。我更新了下面的答案以解决该问题,花了很长时间才找到答案。

您也可以使用其中一种混合模式kCGBlendModeSourceIn/DestinationIn代替CGContextClipToMask.

如果你想创建一个UIImage,下面的每个代码段都可以被下面的代码包围:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, myIconImage.scale); // for correct resolution on retina, thanks @MobileVet
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextTranslateCTM(context, 0, myIconImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);

// image drawing code here

UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

所以这里是用 着色透明图像的代码kCGBlendModeColor

// draw black background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeNormal);
[[UIColor blackColor] setFill];
CGContextFillRect(context, rect);

// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeColor);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

如果您的图像没有半透明像素,您也可以通过以下方式进行操作kCGBlendModeLuminosity

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// replace luminosity of background (ignoring alpha)
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

如果你不关心亮度,因为你刚刚得到了一个应该用颜色着色的 alpha 通道的图像,你可以用一种更有效的方式来做到这一点:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

或相反:

// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);

// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

玩得开心!

于 2011-09-11T11:12:23.867 回答
21

这种方法我取得了最大的成功,因为我尝试的其他方法导致某些颜色组合的半透明像素的颜色失真。这在性能方面也应该更好一些。

+ (UIImage *) imageNamed:(NSString *) name withTintColor: (UIColor *) tintColor {

    UIImage *baseImage = [UIImage imageNamed:name];

    CGRect drawRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);

    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextTranslateCTM(context, 0, baseImage.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // draw original image
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, drawRect, baseImage.CGImage);

    // draw color atop
    CGContextSetFillColorWithColor(context, tintColor.CGColor);
    CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
    CGContextFillRect(context, drawRect);

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return tintedImage;
}
于 2014-03-20T09:21:37.453 回答
18

在四处搜索之后,到目前为止我找到的最佳解决方案是使用混合模式和剪贴蒙版的组合来实现对透明 PNG 的着色/着色:

CGContextSetBlendMode (context, kCGBlendModeMultiply);

CGContextDrawImage(context, rect, myIconImage.CGImage);

CGContextClipToMask(context, rect, myIconImage.CGImage);

CGContextSetFillColorWithColor(context, tintColor);

CGContextFillRect(context, rect);
于 2010-08-19T18:13:35.350 回答
16

通过使用 kCGBlendModeOverlay,我可以获得非常接近 Apple 导航栏中色调的结果。在这篇文章https://stackoverflow.com/a/4684876/229019中采用出色的@fabb 答案并结合@omz 方法,我提供了这个解决方案,它拥有我期望的结果:

- (UIImage *)tintedImageUsingColor:(UIColor *)tintColor;
{
    UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]);

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    // draw original image
    [self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];

    // tint image (loosing alpha). 
    // kCGBlendModeOverlay is the closest I was able to match the 
    // actual process used by apple in navigation bar 
    CGContextSetBlendMode(context, kCGBlendModeOverlay);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    // mask by alpha values of original image
    [self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];

    UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return tintedImage;
}

这是一个用透明度为几个灰度图像着色的示例:

渲染示例

  • 第一行是着色为 [UIColor orangeColor] 的苹果工具栏。
  • 第二行是用几种颜色着色的相同渐变,以清晰的颜色(=实际渐变)开始,以相同的橙色结束。
  • 第三个是带透明度的简单圆圈(亚麻是背景色)
  • 第四行是复杂的暗噪纹理
于 2012-03-29T15:15:27.760 回答
8

您可以创建一个 UIImage 类别并这样做:

- (instancetype)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = (CGRect){ CGPointZero, self.size };
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    [self drawInRect:rect];

    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    UIImage *image  = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
于 2013-09-26T10:46:02.837 回答
8

在 iOS7 中,他们在 UIImageView 上引入了 tintColor 属性,在 UIImage 上引入了 renderingMode。请参阅我在https://stackoverflow.com/a/19125120/1570970上的示例

于 2013-10-01T20:21:58.340 回答
3

请注意,在 fabb 接受的答案中,用于制作 UIImage 的“周围”代码给了我视网膜屏幕上错误的图像分辨率。要修复,请更改:

UIGraphicsBeginImageContext(myIconImage.size);

到:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

最后一个设置为 0.0 的参数是比例,根据 Apple 文档:

“如果指定值 0.0,则比例因子设置为设备主屏幕的比例因子”。

没有评论的权限,编辑似乎有点粗鲁,所以我在回答中提到了这一点。以防万一有人遇到同样的问题。

于 2013-07-11T13:08:12.300 回答
3

在 iOS 7.0 中,您也可以这样做来为基本的 UIImageView 着色:

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];    
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
于 2015-04-30T11:50:50.170 回答
2

UIImageView(或任何与此相关的视图)的背景颜色为 RGBA。颜色中的 alpha 可以满足您的需要,而无需发明新的东西。

于 2010-08-18T18:56:12.753 回答
1

不是我的工作,但我已经成功地使用了这种方法:

http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

于 2012-03-14T07:46:47.210 回答
1

我想在我的自定义 UIButton 子类中为我的图像视图着色,而其他解决方案并没有达到我想要的效果。我需要使图像颜色变暗“色调”。以下是使用 CoreImage 更改亮度的方法。

按下按钮时带有阴影图像的自定义按钮

  1. 确保将 CoreImage.framework 添加到项目的库中。(将二进制文件与库链接)

  2. UIImage 阴影方法

    - (UIImage *)shadeImage:(UIImage *)image {
     CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
    
     CIContext *context = [CIContext contextWithOptions:nil];
    
     CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"
                                  keysAndValues:kCIInputImageKey, inputImage,
                        @"inputBrightness", [NSNumber numberWithFloat:-.5], nil];
     CIImage *outputImage = [filter outputImage];
    
     CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
     UIImage *newImage = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
     CGImageRelease(cgImage);
    
     return newImage;
    }
    
  3. 您需要将上下文的副本存储为 ivar,而不是重新创建它。

于 2013-07-24T16:51:38.063 回答
1

没有答案可以帮助我解决堆栈溢出问题:我们的设计师使用各种形式、各种 alpha 值(和“alpha-holes”)绘制 UI 元素。在大多数情况下,这是带有 alpha 通道的 32 位 PNG 文件,其中包含黑白像素(所有可能的强度)。在为这样的图片着色后,我必须得到这个着色结果:白色像素 - 着色更多,而暗像素 - 更少。而这一切都是针对 Alpha 通道的。我为我的 UIImage 类别编写了这个方法。也许它效率不高,但它作为时钟工作:) 它是:

- (UIImage *)imageTintedWithColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    CGContextSetBlendMode(context, kCGBlendModeCopy);
    [color setFill];
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeXOR alpha:1.0];

    CGContextSetBlendMode(context, kCGBlendModeXOR);
    CGContextFillRect(context, rect);

    [self drawInRect:rect blendMode:kCGBlendModeMultiply alpha:1.0];

    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return coloredImage;
}
于 2016-01-26T15:32:10.230 回答
0

首先,我要感谢 fabb 出色的解决方案,它帮助我完成了为半透明图标着色的任务。因为我需要 C# (Monotouch) 的解决方案,所以我不得不翻译他的代码。这是我想出的。只需将其复制粘贴到您的代码中,然后添加您的半透明图像并完成。

因此,所有的功劳都归于 fabb。这只是为了启动 C# 用户:)

//TINT COLOR IMAGE
UIImageView iImage = new UIImageView(new RectangleF(12, 14, 24,24));
iImage.ContentMode = UIViewContentMode.ScaleAspectFit;
iImage.Image = _dataItem.Image[0] as UIImage;

UIGraphics.BeginImageContextWithOptions(iImage.Bounds.Size, false, UIScreen.MainScreen.Scale);
CGContext context = UIGraphics.GetCurrentContext();

context.TranslateCTM(0, iImage.Bounds.Size.Height);
context.ScaleCTM(1.0f, -1.0f);

RectangleF rect = new RectangleF(0,0, iImage.Bounds.Width, iImage.Bounds.Height);

// draw black background to preserve color of transparent pixels
context.SetBlendMode(CGBlendMode.Normal);
UIColor.Black.SetFill();
context.FillRect(rect);

// draw original image
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, iImage.Image.CGImage);

// tint image (loosing alpha) - the luminosity of the original image is preserved
context.SetBlendMode(CGBlendMode.Color);
UIColor.Orange.SetFill();
context.FillRect(rect);

// mask by alpha values of original image
context.SetBlendMode(CGBlendMode.DestinationIn);
context.DrawImage(rect, iImage.Image.CGImage);

UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();

iImage = new UIImageView(coloredImage);
iImage.Frame = new RectangleF(12, 14, 24,24);
//END TINT COLOR IMAGE

cell.Add(iImage);
于 2013-01-15T10:08:27.147 回答