0%

利用mask实现icon颜色变换

假设你有一张图片如下:

在不同的场景下你需要将这个图标显示成不同的颜色,特别是对于有皮肤功能的app来说,这种场景相对多一些。效果如下:

该如何实现呢?本文提供三种实现方法。如果各位看官有更好的实现方法,欢迎留言讨论。

SampleCode on Github

方法一:利用tintColor和renderingMode

UIImage中有一个属性renderingMode,其中一个可能的取值为:UIImageRenderingModeAlwaysTemplate。官方文档中指出:

UIImageRenderingModeAlwaysTemplate
Always draw the image as a template image, ignoring its color information.

通俗地讲,当UIImage的renderingMode为UIImageRenderingModeAlwaysTemplate时,渲染时不再按照原始图片中的颜色进行渲染,颜色的取值统一取view中tintColor。利用这个特性,则就可以实现我们想要的效果啦,代码如下。

1
2
3
4
5
6
7
8
9
10
- (UIImageView *)imageViewWithSourceImage:(UIImage *)image tintColor:(UIColor *)color
{
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
CGRect frame = (CGRect){CGPointZero, image.size};;
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:frame] autorelease];
imageView.tintColor = color;
imageView.image = image;

return imageView;
}

方法二:利用layer.mask

CALayer有一个属性mask。对于每一个像素点的alpha值,可以用这个公式计算得出:

1
content.alpha = originContent.alpha * mask.alpha;

利用mask和设置backgroundColor为我们想要的颜色,同样也可以实现。

1
2
3
4
5
6
7
8
9
10
11
12
- (UIImageView *)imageViewWithSourceImage:(UIImage *)image layerColor:(UIColor *)color
{
CGRect frame = (CGRect){CGPointZero, image.size};;
UIImageView *imageView = [[[UIImageView alloc] initWithFrame:frame] autorelease];
CALayer *layer = [CALayer layer];
layer.frame = imageView.bounds;
layer.contents = (id)image.CGImage;
imageView.layer.mask = layer;
imageView.backgroundColor = color;

return imageView;
}

这里需要注意的是,不要忘记设置mask的frame

方法三:CoreGraphics绘制

主要利用CGContextClipToMask方法,将填充区域限制在mask的范围内,再填充颜色即可。
需要注意的是,CG的坐标系和UIKit的坐标系是不同的。UIKit以左上角为原点;CG以左下角为原点,y轴向上。绘制的时候需要注意坐标轴变换。
其次,要注意image的scale处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
- (UIImageView *)imageViewWithSourceImage:(UIImage *)image fillColor:(UIColor *)color
{
CGRect rect = (CGRect){CGPointZero, image.size};

UIGraphicsBeginImageContextWithOptions(rect.size, NO, image.scale);
CGContextRef context = UIGraphicsGetCurrentContext();

// 由于坐标系不同,需要翻转Y坐标。如果不翻转Y坐标,则image上下颠倒。
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.f, -1.f);

// 将后续的操作都限制到mask范围内
CGContextClipToMask(context, rect, image.CGImage);

// 填充颜色
CGContextSetFillColorWithColor(context, color.CGColor);
CGContextFillRect(context, rect);

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

UIImageView *imageView = [[[UIImageView alloc] initWithFrame:rect] autorelease];
imageView.image = sourceImage;

return imageView;
}