ios 如何在 iPhone 中为透明的 PNG 图像着色?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3514066/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to tint a transparent PNG image in iPhone?
提问by anna
I know it's possible to tint a rectangular image by drawing a CGContextFillRect over it and setting the blend mode. However, I can't figure out how to do a tint on a transparent image such as an icon. It must be possible since the SDK does it itself on tab-bars in such. Would anyone be able to provide a snippet?
我知道可以通过在矩形图像上绘制一个 CGContextFillRect 并设置混合模式来为它着色。但是,我不知道如何对透明图像(例如图标)进行着色。这一定是可能的,因为 SDK 本身就是在标签栏上这样做的。有人能提供一个片段吗?
UPDATE:
更新:
Lots of great suggestions have been given for this problem since I originally asked. Be sure to read through all the answers to figure out what suits you best.
自从我最初提出这个问题以来,已经为这个问题提供了很多很好的建议。请务必通读所有答案,以找出最适合您的答案。
UPDATE (Apr 30, 2015):
更新(2015 年 4 月 30 日):
With iOS 7.0, I can now just do the following, which would satisfy the needs of my original question. But if you have more complicated cases, check out all the answers.
使用 iOS 7.0,我现在可以执行以下操作,这将满足我原始问题的需求。但如果您有更复杂的情况,请查看所有答案。
UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
回答by fabb
Update:Here is a Gistfor a Swift UIColor extension using the code below.
更新:这是使用以下代码的 Swift UIColor 扩展的要点。
If you have a greyscale imageand want white become the tinting color, kCGBlendModeMultiply
is the way to go. With this method, you cannot have highlights lighter than your tinting color.
如果您有灰度图像并希望白色成为着色颜色,kCGBlendModeMultiply
则是要走的路。使用此方法,您的高光不能比着色颜色浅。
On the contrary, if you have either a non-greyscale image, ORyou have highlights andshadowsthat should be preserved, the blend mode kCGBlendModeColor
is the way to go. White will stay white and black will stay black as the lightness of the image is preserved. This mode is just made for tinting - it is the same as Photoshop's Color
layer blend mode (disclaimer: slightly differing results may happen).
相反,如果你有一张非灰度图像,或者你有亮点和阴影应保留,混合模式kCGBlendModeColor
是要走的路。白色将保持白色,黑色将保持黑色,因为图像的亮度得以保留。此模式仅用于着色 - 它与 Photoshop 的Color
图层混合模式相同(免责声明:可能会出现略有不同的结果)。
Note that tinting alpha-pixels does not work correctly neither in iOS nor in Photoshop - half-transparent black pixels would not stay black. I updated the answer below to work around that issue, it took quite a long time to find out.
请注意,无论是在 iOS 还是在 Photoshop 中,着色 alpha 像素都无法正常工作 - 半透明的黑色像素不会保持黑色。我更新了下面的答案来解决这个问题,花了很长时间才找到答案。
You can also use one of the blend modes kCGBlendModeSourceIn/DestinationIn
instead of CGContextClipToMask
.
您还可以使用其中一种混合模式kCGBlendModeSourceIn/DestinationIn
代替CGContextClipToMask
。
If you want to create a UIImage
, each of the following code sections can be surrounded by the following code:
如果要创建一个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();
So here's the code for tinting a transparent image with kCGBlendModeColor
:
所以这是使用以下代码为透明图像着色的代码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);
If your image has no half-transparent pixels, you could also do it the other way around with kCGBlendModeLuminosity
:
如果您的图像没有半透明像素,您也可以使用以下方法反过来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);
If you don't care for luminosity, as you just have got an image with an alpha channel that should be tinted with a color, you can do it in a more efficient way:
如果您不关心亮度,因为您刚刚获得了一个带有 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);
or the other way around:
或者反过来:
// 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);
Have fun!
玩得开心!
回答by cania
I had most success with this method, because the others I tried caused distorted colors for semi-transparent pixels for certain color-combinations. This should also be a bit better on the performance side.
我用这种方法取得了最大的成功,因为我尝试过的其他方法会导致某些颜色组合的半透明像素的颜色失真。这在性能方面也应该好一点。
+ (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;
}
回答by anna
After searching around, the best solution I've come to thus far is to use a combination of blend mode and the clipping mask to achieve colorizing/tinting a transparent PNG:
在四处搜索之后,我到目前为止找到的最佳解决方案是使用混合模式和剪贴蒙版的组合来实现对透明 PNG 的着色/着色:
CGContextSetBlendMode (context, kCGBlendModeMultiply);
CGContextDrawImage(context, rect, myIconImage.CGImage);
CGContextClipToMask(context, rect, myIconImage.CGImage);
CGContextSetFillColorWithColor(context, tintColor);
CGContextFillRect(context, rect);
回答by tozevv
I can get results very close to the tint in the Apple navigation bar by using kCGBlendModeOverlay. Taking excelent @fabb answer and combining @omz approach in this post https://stackoverflow.com/a/4684876/229019I came with this solution that helds the results I was expecting:
通过使用 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;
}
Here's an example tinting several grayscale images with transparency:
这里有一个例子着色的透明度几个灰度图像:
:
:
- The first line is the apple toolbar tinted [UIColor orangeColor].
- The second line is the same gradient tinted in several colors starting with clear color (= the actual gradient) and ending with the same orange.
- The third is a simple circle with transparency (the linen is the background color)
- The forth line is a complex dark noisy texture
- 第一行是苹果工具栏[UIColor orangeColor]。
- 第二行是相同的渐变,以几种颜色着色,从清晰的颜色(=实际渐变)开始,以相同的橙色结束。
- 第三个是一个简单的透明圆圈(亚麻色是背景色)
- 第四行是复杂的暗噪纹理
回答by kkodev
You could create an UIImage category and do it like this:
您可以创建一个 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;
}
回答by Toland Hon
In iOS7, they've introduced tintColor property on UIImageView and renderingMode on UIImage. See my example at https://stackoverflow.com/a/19125120/1570970
在 iOS7 中,他们在 UIImageView 上引入了 tintColor 属性,在 UIImage 上引入了 renderMode 属性。在https://stackoverflow.com/a/19125120/1570970 上查看我的示例
回答by anna
With iOS 7.0, you can also just do this to tint a basic UIImageView:
在 iOS 7.0 中,您也可以这样做来为基本的 UIImageView 着色:
UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
回答by lxl
Note that in the accepted answer by fabb, the "surrounding" code for making a UIImage gave me the wrong resolution of images on retina screen. To fix, change:
请注意,在 fabb 接受的答案中,制作 UIImage 的“周围”代码给了我视网膜屏幕上错误的图像分辨率。要修复,请更改:
UIGraphicsBeginImageContext(myIconImage.size);
to:
到:
UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);
The last parameter which is set to 0.0 is scale, and according to Apple documentation:
设置为 0.0 的最后一个参数是比例,根据 Apple 文档:
"If you specify a value of 0.0, the scale factor is set to the scale factor of the device's main screen".
“如果您指定的值为 0.0,则比例因子将设置为设备主屏幕的比例因子”。
Dont have the permission to comment, and editing seems a bit rude, so I mention this in an answer. Just in case someone encounters this same problem.
没有评论权限,编辑似乎有点粗鲁,所以我在回答中提到了这一点。以防万一有人遇到同样的问题。
回答by NWCoder
UIImageView (or any view for that matter) has a background color which is RGBA. The alpha in the color may do what you need without inventing something new.
UIImageView(或任何与此相关的视图)的背景颜色为 RGBA。颜色中的 alpha 可以满足您的需求,而无需发明新的东西。
回答by Dermot
Not my work, but i've successfully used this approach:
不是我的工作,但我已经成功地使用了这种方法:
http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage
http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage