【问题标题】:How would I tint an image programmatically on iOS?如何在 iOS 上以编程方式为图像着色?
【发布时间】:2016-12-18 13:53:54
【问题描述】:

我想用颜色参考为图像着色。结果应该类似于 Photoshop 中的正片叠底混合模式,其中 whites 将被替换为 tint

我会不断改变颜色值。

跟进:我会将执行此操作的代码放在 ImageView 的 drawRect: 方法中,对吧?

与往常一样,代码 sn-p 将极大地帮助我理解,而不是链接。

更新:使用建议的代码 Ramin 子类化 UIImageView。

我把它放在我的视图控制器的 viewDidLoad: 中:

[self.lena setImage:[UIImage imageNamed:kImageName]];
[self.lena setOverlayColor:[UIColor blueColor]];
[super viewDidLoad];

我看到了图像,但它没有被着色。我还尝试加载其他图像,在 IB 中设置图像,并在我的视图控制器中调用 setNeedsDisplay:。

更新:drawRect:没有被调用。

最终更新:我发现了一个旧项目,它的 imageView 设置正确,因此我可以测试 Ramin 的代码,它就像一个魅力!

最终的、最终的更新:

对于那些刚刚学习 Core Graphics 的人来说,这是最简单的方法。

在您的子类 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
}

【问题讨论】:

  • 我添加了 sn-p,然后将其发布到我坐在那里的一些旧的 drawRect 代码中,它运行良好。可能想在没有 [super viewDidLoad] 调用的情况下尝试它(或将其移至上方)。还要仔细检查以确保分配此对象的人分配的是派生版本而不是普通 UIImageView(即,如果它加载到 nib、分配器等中)。
  • 如果上述方法不起作用,另一个建议是:不要将 UIImageView 子类化为普通 UIView 并添加可以设置的 overlayColor 和名为“image”的 UIImage 属性。然后把drawRect放在那里。 drawRect 代码不关心 'image' 值是来自 UIImageView 还是来自您定义的属性。
  • 如果图像带有 alpha,这不会失败吗?如果我只想为图像的绘制部分着色怎么办? (就像 UIButton 一样?)

标签: ios cocoa-touch image-processing uiimage tint


【解决方案1】:

为此我制作了宏:

#define removeTint(view) \
if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\
for (CALayer *layer in [view.layer sublayers]) {\
if ([((NSNumber *)[layer valueForKey:@"__isTintLayer"]) boolValue]) {\
[layer removeFromSuperlayer];\
break;\
}\
}\
}

#define setTint(view, tintColor) \
{\
if ([((NSNumber *)[view.layer valueForKey:@"__hasTint"]) boolValue]) {\
removeTint(view);\
}\
[view.layer setValue:@(YES) forKey:@"__hasTint"];\
CALayer *tintLayer = [CALayer new];\
tintLayer.frame = view.bounds;\
tintLayer.backgroundColor = [tintColor CGColor];\
[tintLayer setValue:@(YES) forKey:@"__isTintLayer"];\
[view.layer addSublayer:tintLayer];\
}

要使用,只需调用:

setTint(yourView, yourUIColor);
//Note: include opacity of tint in your UIColor using the alpha channel (RGBA), e.g. [UIColor colorWithRed:0.5f green:0.0 blue:0.0 alpha:0.25f];

去除色调时,只需调用:

removeTint(yourView);

【讨论】:

    【解决方案2】:

    对于 Swift 2.0,

        let image: UIImage! = UIGraphicsGetImageFromCurrentImageContext()
    
        imgView.image = imgView.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
    
        imgView.tintColor = UIColor(red: 51/255.0, green: 51/255.0, blue: 
    
    51/255.0, alpha: 1.0)
    

    【讨论】:

      【解决方案3】:

      只是一个快速的澄清(在对该主题进行一些研究之后)。 Apple 文档here 明确指出:

      UIImageView 类经过优化,可以将其图像绘制到显示器上。 UIImageView 不调用其子类的 drawRect: 方法。如果您的子类需要包含自定义绘图代码,则应改为将UIView 类子类化。

      所以甚至不要浪费任何时间尝试在UIImageView 子类中覆盖该方法。请改用UIView 开头。

      【讨论】:

      • 哦,我多么希望六个月前就知道这一点。他们应该将警告放在 72 点类型中,红色带有闪烁标签。
      • 我知道你的意思是......我浪费了半天时间。
      • 谢谢@mpstx ...还将该文档行放在引号中的答案中...Apple应该在文档中做同样的事情...:-)
      【解决方案4】:
      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
      

      【讨论】:

      • 我需要为 UIImage 着色,但不是在 UIImageView 中使用它,而是作为 UINavigationBar 的背景图像,而您的代码成功了。谢谢。
      【解决方案5】:

      我想用 alpha 为图像着色,并创建了以下类。如果您发现任何问题,请告诉我。

      我已将我的类命名为CSTintedImageView,它继承自UIView,因为UIImageView 不调用drawRect: 方法,就像之前的回复中提到的那样。 我设置了一个指定的初始化器,类似于UIImageView 类中的初始化器。

      用法:

      CSTintedImageView * imageView = [[CSTintedImageView alloc] initWithImage:[UIImage imageNamed:@"image"]];
      imageView.tintColor = [UIColor redColor];
      

      CSTintedImageView.h

      @interface CSTintedImageView : UIView
      
      @property (strong, nonatomic) UIImage * image;
      @property (strong, nonatomic) UIColor * tintColor;
      
      - (id)initWithImage:(UIImage *)image;
      
      @end
      

      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
      

      【讨论】:

      • 谢谢。这是该问题中第一个真正正确支持图像透明度的解决方案。
      • 当我尝试使用这个答案时,我的背景(应该是透明的区域)是黑色的。你知道我在这里做错了什么吗?
      • 没关系! (我在情节提要中设置了背景颜色!D'oh!)这个解决方案太棒了!!!
      • 这个解决方案效果很好,虽然我通过先渲染图像得到了更好的结果,然后用 kCGBlendModeColor 在顶部填充颜色。
      • drawRect: 方法确实是这个解决方案的“肉”。剩下的就是品味/风格的问题。谢谢!为我工作!
      【解决方案6】:

      在 iOS7 中,他们在 UIImageView 上引入了 tintColor 属性,在 UIImage 上引入了 renderingMode。要在 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
      

      【讨论】:

      • 这绝对是在 iOS 7 上最好和最简单的方法 如果 这是您正在寻找的色调行为。但是这段代码应用了一种色调,就好像您在 Photoshop 中使用了 100% 不透明的“叠加”混合模式,而不是原始问题正在寻找的“正片叠底”混合模式。
      【解决方案7】:

      这是实现图像着色的另一种方法,特别是如果您已经将 QuartzCore 用于其他用途。这是我对a similar question 的回答。

      导入 QuartzCore:

      #import <QuartzCore/QuartzCore.h>
      

      创建透明的 CALayer 并将其添加为要着色的图像的子层:

      CALayer *sublayer = [CALayer layer];
      [sublayer setBackgroundColor:[UIColor whiteColor].CGColor];
      [sublayer setOpacity:0.3];
      [sublayer setFrame:toBeTintedImage.frame];
      [toBeTintedImage.layer addSublayer:sublayer];
      

      将 QuartzCore 添加到您的项目框架列表中(如果它不存在),否则您将收到如下编译器错误:

      Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
      

      【讨论】:

        【解决方案8】:

        我为此开源了一个库:ios-image-filters

        【讨论】:

          【解决方案9】:

          对于那些尝试子类化 UIImageView 类并卡在“drawRect: is not being called”的人,请注意,您应该改为子类化 UIView 类,因为对于 UIImageView 类,“drawRect:”方法不是叫。在这里阅读更多:drawRect not being called in my subclass of UIImageView

          【讨论】:

            【解决方案10】:

            这可能非常有用:PhotoshopFramework 是一个在 Objective-C 上处理图像的强大库。这是为了带来 Adob​​e Photoshop 用户熟悉的相同功能。示例:使用 RGB 0-255 设置颜色、应用混合文件、转换...

            是开源的,这里是项目链接:https://sourceforge.net/projects/photoshopframew/

            【讨论】:

            • 看起来很有趣,但是当 Adob​​e 的律师发现这件事时,你打算把名字改成什么?
            • 我们稍后会弄清楚。反正不是商业产品,是某种“代号”。也是一个内部库。
            • +1 推荐一个图书馆(即使它是一个小小的自我推销),并且没有给出重新发明轮子的步骤。
            【解决方案11】:

            首先,您需要继承 UIImageView 并覆盖 drawRect 方法。您的类需要一个 UIColor 属性(我们称之为 overlayColor)来保存混合颜色和一个自定义设置器,该设置器在颜色更改时强制重绘。像这样的:

            - (void) setOverlayColor:(UIColor *)newColor {
               if (overlayColor)
                 [overlayColor release];
            
               overlayColor = [newColor retain];
               [self setNeedsDisplay]; // fires off drawRect each time color changes
            }
            

            在 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);
            }
            

            通常,为了优化绘图,您会将实际绘图限制为仅传递给 drawRect 的区域,但由于每次颜色更改时都必须重新绘制背景图像,因此整个事物可能需要刷新。

            要使用它,请创建对象的实例,然后将image 属性(继承自 UIImageView)设置为图片并将overlayColor 设置为 UIColor 值(可以通过更改颜色的 alpha 值来调整混合级别你传下去)。

            【讨论】:

            • 原始请求附带的后续建议(上图)。
            • 应该添加CGContextSaveGState(context);在更改混合模式之前,否则会出现 gstack 下溢。
            • 哦,谢谢。复制/粘贴错误。我已经在代码 sn-p 中修复了它。
            • (如另一个答案中所述)特殊注意事项 UIImageView 类经过优化,可将其图像绘制到显示器上。 UIImageView 不会调用 drawRect: 一个子类。如果您的子类需要自定义绘图代码,建议您使用 UIView 作为基类。 这意味着您不能继承 UIImageView 并期望调用 drawRect,根本
            • 嗨,不幸的是我尝试了代码但它没有工作:/。请参阅@mpstx 下面的答案,它说不会从 UIImageView 调用 drawRect,而是应该使用对我来说很好的 UIView(带有 UIImage)。
            【解决方案12】:

            此外,您可能还需要考虑缓存合成图像以提高性能并仅在 drawRect: 中渲染它,然后在脏标志确实脏时更新它。虽然您可能会经常更改它,但在某些情况下可能会出现绘图并且您并不脏,因此您可以简单地从缓存中刷新。如果内存比性能更重要,你可以忽略这个:)

            【讨论】:

            • 这就是底层 CALayer 自动为您做的事情。无需手动缓存视图图纸。
            【解决方案13】:

            我唯一能想到的就是创建一个具有所需颜色的矩形大部分透明视图,然后通过将其添加为子视图将其放置在您的图像视图上。我不确定这是否真的会按照您想象的方式对图像进行着色,但我不确定您将如何破解图像并选择性地用其他颜色替换某些颜色......对我来说听起来很雄心勃勃。

            例如:

            UIImageView *yourPicture = (however you grab the image);
            UIView *colorBlock = [[UIView alloc] initWithFrame:yourPicture.frame];
            //Replace R G B and A with values from 0 - 1 based on your color and transparency
            colorBlock.backgroundColor = [UIColor colorWithRed:R green:G blue:B alpha:A];
            [yourPicture addSubView:colorBlock];
            

            UIColor 的文档:

            colorWithRed:green:blue:alpha:
            
            Creates and returns a color object using the specified opacity and RGB component values.
            
            + (UIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha
            
            Parameters
            
            red    - The red component of the color object, specified as a value from 0.0 to 1.0.
            
            green  - The green component of the color object, specified as a value from 0.0 to 1.0.
            
            blue   - The blue component of the color object, specified as a value from 0.0 to 1.0.
            
            
            
            alpha  - The opacity value of the color object, specified as a value from 0.0 to 1.0.
            
            Return Value
            
            The color object. The color information represented by this object is in the device RGB colorspace. 
            

            【讨论】:

            • Core Graphics 似乎能够应用弯曲模式。如何,是个问题。
            猜你喜欢
            • 1970-01-01
            • 2014-04-01
            • 2014-01-07
            • 2013-01-08
            • 2016-08-22
            • 2023-03-31
            • 2011-06-16
            • 2021-10-13
            相关资源
            最近更新 更多