如何在 iOS 上以编程方式为图像着色?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1117211/
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 would I tint an image programmatically on iOS?
提问by willc2
I would like to tint an image with a color reference. The results should look like the Multiply blending mode in Photoshop, where whiteswould be replaced with tint:
我想用颜色参考为图像着色。结果应该类似于 Photoshop 中的 Multiply 混合模式,其中白色将替换为tint:
I will be changing the color value continuously.
我将不断改变颜色值。
Follow up:I would put the code to do this in my ImageView's drawRect: method, right?
跟进:我会将代码放在我的 ImageView 的 drawRect: 方法中,对吗?
As always, a code snippetwould greatly aid in my understanding, as opposed to a link.
与往常一样,代码片段对我的理解有很大帮助,而不是链接。
Update:Subclassing a UIImageView with the code Raminsuggested.
更新:使用Ramin建议的代码对 UIImageView 进行子类化。
I put this in viewDidLoad: of my view controller:
我把它放在我的视图控制器的 viewDidLoad: 中:
[self.lena setImage:[UIImage imageNamed:kImageName]];
[self.lena setOverlayColor:[UIColor blueColor]];
[super viewDidLoad];
I see the image, but it is not being tinted. I also tried loading other images, setting the image in IB, and calling setNeedsDisplay: in my view controller.
我看到图像,但它没有被着色。我还尝试加载其他图像,在 IB 中设置图像,并在我的视图控制器中调用 setNeedsDisplay:。
Update: drawRect: is not being called.
更新:drawRect:没有被调用。
Final update:I found an old project that had an imageView set up properly so I could test Ramin's code and it works like a charm!
最终更新:我发现了一个旧项目,它的 imageView 设置正确,所以我可以测试 Ramin 的代码,它的工作原理非常棒!
Final, final update:
最后,最后更新:
For those of you just learning about Core Graphics, here is the simplest thing that could possibly work.
对于那些刚刚学习 Core Graphics 的人来说,这里是最简单的可能可行的方法。
In your subclassed UIView:
在您的子类 UIView 中:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColor(context, CGColorGetComponents([UIColor colorWithRed:0.5 green:0.5 blue:0 alpha:1].CGColor)); // don't make color too saturated
CGContextFillRect(context, rect); // draw base
[[UIImage imageNamed:@"someImage.png"] drawInRect: rect blendMode:kCGBlendModeOverlay alpha:1.0]; // draw image
}
采纳答案by Ramin
First you'll want to subclass UIImageView and override the drawRect method. Your class needs a UIColor property (let's call it overlayColor) to hold the blend color and a custom setter that forces a redraw when the color changes. Something like this:
首先,您需要继承 UIImageView 并覆盖 drawRect 方法。您的类需要一个 UIColor 属性(我们称之为overlayColor)来保存混合颜色和一个在颜色改变时强制重绘的自定义设置器。像这样的东西:
- (void) setOverlayColor:(UIColor *)newColor {
if (overlayColor)
[overlayColor release];
overlayColor = [newColor retain];
[self setNeedsDisplay]; // fires off drawRect each time color changes
}
In the drawRect method you'll want to draw the image first then overlay it with a rectangle filled with the color you want along with the proper blending mode, something like this:
在 drawRect 方法中,您需要先绘制图像,然后用一个矩形覆盖它,该矩形填充了您想要的颜色以及适当的混合模式,如下所示:
- (void) drawRect:(CGRect)area
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// Draw picture first
//
CGContextDrawImage(context, self.frame, self.image.CGImage);
// Blend mode could be any of CGBlendMode values. Now draw filled rectangle
// over top of image.
//
CGContextSetBlendMode (context, kCGBlendModeMultiply);
CGContextSetFillColor(context, CGColorGetComponents(self.overlayColor.CGColor));
CGContextFillRect (context, self.bounds);
CGContextRestoreGState(context);
}
Ordinarily to optimize the drawing you would restrict the actual drawing to only the area passed in to drawRect, but since the background image has to be redrawn each time the color changes it's likely the whole thing will need refreshing.
通常为了优化绘图,您会将实际绘图限制为仅传递给 drawRect 的区域,但由于每次颜色更改时都必须重新绘制背景图像,因此整个事情可能需要刷新。
To use it create an instance of the object then set the image
property (inherited from UIImageView) to the picture and overlayColor
to a UIColor value (the blend levels can be adjusted by changing the alpha value of the color you pass down).
要使用它,请创建一个对象实例,然后将image
属性(从 UIImageView 继承)设置为图片和overlayColor
UIColor 值(可以通过更改传递的颜色的 alpha 值来调整混合级别)。
回答by Toland Hon
In iOS7, they've introduced tintColor property on UIImageView and renderingMode on UIImage. To tint an UIImage on iOS7, all you have to do is:
在 iOS7 中,他们在 UIImageView 上引入了 tintColor 属性,在 UIImage 上引入了 renderMode。要在 iOS7 上为 UIImage 着色,您所要做的就是:
UIImageView* imageView = …
UIImage* originalImage = …
UIImage* imageForRendering = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imageView.image = imageForRendering;
imageView.tintColor = [UIColor redColor]; // or any color you want to tint it with
回答by mattorb
Just a quick clarification (after some research on this topic). The Apple doc hereclearly states that:
只是一个快速的澄清(在对这个主题进行一些研究之后)。此处的 Apple 文档明确指出:
The
UIImageView
class is optimized to draw its images to the display.UIImageView
does not call thedrawRect:
method of its subclasses. If your subclass needs to include custom drawing code, you should subclass theUIView
class instead.
所述
UIImageView
类被优化以绘制其图像的显示。UIImageView
不调用drawRect:
其子类的方法。如果您的子类需要包含自定义绘图代码,则应UIView
改为子类化该类。
so don't even waste any time attempting to override that method in a UIImageView
subclass. Start with UIView
instead.
所以不要浪费任何时间尝试在UIImageView
子类中覆盖该方法。开始UIView
代替。
回答by mattorb
I wanted to tint an image with alpha and I created the following class. Please let me know if you find any problems with it.
我想用 alpha 为图像着色,我创建了以下类。如果您发现任何问题,请告诉我。
I have named my class CSTintedImageView
and it inherits from UIView
since UIImageView
does not call the drawRect:
method, like mentioned in previous replies.
I have set a designated initializer similar to the one found in the UIImageView
class.
我已经命名了我的类CSTintedImageView
,它继承自,UIView
因为UIImageView
没有调用该drawRect:
方法,就像之前的回复中提到的那样。我已经设置了一个类似于在UIImageView
类中找到的指定初始值设定项。
Usage:
用法:
CSTintedImageView * imageView = [[CSTintedImageView alloc] initWithImage:[UIImage imageNamed:@"image"]];
imageView.tintColor = [UIColor redColor];
CSTintedImageView.h
CSTintedImageView.h
@interface CSTintedImageView : UIView
@property (strong, nonatomic) UIImage * image;
@property (strong, nonatomic) UIColor * tintColor;
- (id)initWithImage:(UIImage *)image;
@end
CSTintedImageView.m
CSTintedImageView.m
#import "CSTintedImageView.h"
@implementation CSTintedImageView
@synthesize image=_image;
@synthesize tintColor=_tintColor;
- (id)initWithImage:(UIImage *)image
{
self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
if(self)
{
self.image = image;
//set the view to opaque
self.opaque = NO;
}
return self;
}
- (void)setTintColor:(UIColor *)color
{
_tintColor = color;
//update every time the tint color is set
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
//resolve CG/iOS coordinate mismatch
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, -rect.size.height);
//set the clipping area to the image
CGContextClipToMask(context, rect, _image.CGImage);
//set the fill color
CGContextSetFillColor(context, CGColorGetComponents(_tintColor.CGColor));
CGContextFillRect(context, rect);
//blend mode overlay
CGContextSetBlendMode(context, kCGBlendModeOverlay);
//draw the image
CGContextDrawImage(context, rect, _image.CGImage);
}
@end
回答by SEQOY Development Team
This could be very useful: PhotoshopFramework is one powerful library to manipulate images on Objective-C. This was developed to bring the same functionalities that Adobe Photoshop users are familiar. Examples: Set colors using RGB 0-255, apply blend filers, transformations...
这可能非常有用:PhotoshopFramework 是一个强大的库,可以在 Objective-C 上操作图像。这是为了带来 Adobe Photoshop 用户熟悉的相同功能而开发的。示例:使用 RGB 0-255 设置颜色、应用混合过滤器、转换...
Is open source, here is the project link: https://sourceforge.net/projects/photoshopframew/
是开源的,这里是项目链接:https: //sourceforge.net/projects/photoshopframew/
回答by gngrwzrd
UIImage * image = mySourceImage;
UIColor * color = [UIColor yellowColor];
UIGraphicsBeginImageContext(image.size);
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height) blendMode:kCGBlendModeNormal alpha:1];
UIBezierPath * path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, image.size.width, image.size.height)];
[color setFill];
[path fillWithBlendMode:kCGBlendModeMultiply alpha:1]; //look up blending modes for your needs
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//use newImage for something
回答by lekksi
Here is another way to implement image tinting, especially if you are already using QuartzCore for something else. This was my answer for a similar question.
这是实现图像着色的另一种方法,特别是如果您已经将 QuartzCore 用于其他用途。这是我对类似问题的回答。
Import QuartzCore:
导入 QuartzCore:
#import <QuartzCore/QuartzCore.h>
Create transparent CALayer and add it as a sublayer for the image you want to tint:
创建透明 CALayer 并将其添加为要着色的图像的子图层:
CALayer *sublayer = [CALayer layer];
[sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
[sublayer setOpacity:0.3];
[sublayer setFrame:toBeTintedImage.frame];
[toBeTintedImage.layer addSublayer:sublayer];
Add QuartzCore to your projects Framework list (if it isn't already there), otherwise you'll get compiler errors like this:
将 QuartzCore 添加到你的项目框架列表中(如果它还没有),否则你会得到这样的编译器错误:
Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
回答by pierre
For those of you who try to subclass an UIImageView class and get stuck at "drawRect: is not being called", note that you should subclass an UIView class instead, because for UIImageView classes, the "drawRect:" method is not called. Read more here: drawRect not being called in my subclass of UIImageView
对于那些尝试对 UIImageView 类进行子类化并陷入“drawRect: is not being called”的人,请注意,您应该继承 UIView 类,因为对于 UIImageView 类,不会调用“drawRect:”方法。在此处阅读更多信息:drawRect 未在我的 UIImageView 子类中调用
回答by esilver
I have a library I open-sourced for this: ios-image-filters
我有一个为此开源的库:ios-image-filters
回答by Steve Riggins
Also you might want to consider caching the composited image for performance and just rendering it in drawRect:, then updated it if a dirty flag is indeed dirty. While you might be changing it often, there may be cases where draws are coming in and you're not dirty, so you can simply refresh from the cache. If memory is more of an issue than performance, you can ignore this :)
此外,您可能需要考虑缓存合成图像以提高性能并仅在 drawRect: 中渲染它,然后在脏标志确实脏时更新它。虽然您可能经常更改它,但在某些情况下可能会出现绘制并且您不脏,因此您可以简单地从缓存中刷新。如果内存比性能更重要,您可以忽略这一点:)