UIImage color change

I am trying to change the color of a UIImage. My code is:

-(UIImage *)coloredImage:(UIImage *)firstImage withColor:(UIColor *)color { UIGraphicsBeginImageContext(firstImage.size); CGContextRef context = UIGraphicsGetCurrentContext(); [color setFill]; CGContextTranslateCTM(context, 0, firstImage.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextSetBlendMode(context, kCGBlendModeCopy); CGRect rect = CGRectMake(0, 0, firstImage.size.width, firstImage.size.height); CGContextDrawImage(context, rect, firstImage.CGImage); CGContextClipToMask(context, rect, firstImage.CGImage); CGContextAddRect(context, rect); CGContextDrawPath(context,kCGPathElementMoveToPoint); UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return coloredImg; } 

This code works, but the resulting image is not as good as shoud be: the pixel borders of the returned image are intermittent and not as smooth as in my first image. How can I solve this problem?

+81
ios objective-c iphone uiimage cgcontext
Oct 13 '12 at 11:31
source share
12 answers

Starting with iOS 7, this is the easiest way to do this.

Objective-C:

 theImageView.image = [theImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; [theImageView setTintColor:[UIColor redColor]]; 

Swift 2.0:

 theImageView.image = theImageView.image?.imageWithRenderingMode(.AlwaysTemplate) theImageView.tintColor = UIColor.magentaColor() 

Swift 4.0:

 theImageView.image = theImageView.image?.withRenderingMode(.alwaysTemplate) theImageView.tintColor = .magenta 

Storyboard:

First, configure the image as a template (on the right panel - Display as) in your resources. Then the color of the image will match the color of the hue. enter image description here

+222
Nov 17 '14 at 4:31
source share
โ€” -

This is pretty much the answer above, but slightly shortened. It only perceives the image as a mask and actually does not โ€œreproduceโ€ and does not color the image.

Goal C:

  UIColor *color = <# UIColor #>; UIImage *image = <# UIImage #>;// Image to mask with UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale); CGContextRef context = UIGraphicsGetCurrentContext(); [color setFill]; CGContextTranslateCTM(context, 0, image.size.height); CGContextScaleCTM(context, 1.0, -1.0); CGContextClipToMask(context, CGRectMake(0, 0, image.size.width, image.size.height), [image CGImage]); CGContextFillRect(context, CGRectMake(0, 0, image.size.width, image.size.height)); UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 

Swift:

  let color: UIColor = <# UIColor #> let image: UIImage = <# UIImage #> // Image to mask with UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) let context = UIGraphicsGetCurrentContext() color.setFill() context?.translateBy(x: 0, y: image.size.height) context?.scaleBy(x: 1.0, y: -1.0) context?.clip(to: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height), mask: image.cgImage!) context?.fill(CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)) let coloredImg = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() 
+26
03 Oct '13 at 6:57
source share

Another way to tint an image is to simply multiply it by a constant color. This is sometimes preferable because it does not โ€œraiseโ€ color values โ€‹โ€‹in black areas; it keeps the relative image intensities the same. Using an overlay as a hue tends to equalize contrast.

This is the code I'm using:

 UIImage *MultiplyImageByConstantColor( UIImage *image, UIColor *color ) { CGSize backgroundSize = image.size; UIGraphicsBeginImageContext(backgroundSize); CGContextRef ctx = UIGraphicsGetCurrentContext(); CGRect backgroundRect; backgroundRect.size = backgroundSize; backgroundRect.origin.x = 0; backgroundRect.origin.y = 0; CGFloat r,g,b,a; [color getRed:&r green:&g blue:&b alpha:&a]; CGContextSetRGBFillColor(ctx, r, g, b, a); CGContextFillRect(ctx, backgroundRect); CGRect imageRect; imageRect.size = image.size; imageRect.origin.x = (backgroundSize.width - image.size.width)/2; imageRect.origin.y = (backgroundSize.height - image.size.height)/2; // Unflip the image CGContextTranslateCTM(ctx, 0, backgroundSize.height); CGContextScaleCTM(ctx, 1.0, -1.0); CGContextSetBlendMode(ctx, kCGBlendModeMultiply); CGContextDrawImage(ctx, imageRect, image.CGImage); UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return newImage; } 

Quick version

 extension UIImage{ static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{ let backgroundSize = image.size UIGraphicsBeginImageContext(backgroundSize) let ctx = UIGraphicsGetCurrentContext() var backgroundRect=CGRect() backgroundRect.size = backgroundSize backgroundRect.origin.x = 0 backgroundRect.origin.y = 0 var r:CGFloat var g:CGFloat var b:CGFloat var a:CGFloat color.getRed(&r, green: &g, blue: &b, alpha: &a) CGContextSetRGBFillColor(ctx, r, g, b, a) CGContextFillRect(ctx, backgroundRect) var imageRect=CGRect() imageRect.size = image.size imageRect.origin.x = (backgroundSize.width - image.size.width)/2 imageRect.origin.y = (backgroundSize.height - image.size.height)/2 // Unflip the image CGContextTranslateCTM(ctx, 0, backgroundSize.height) CGContextScaleCTM(ctx, 1.0, -1.0) CGContextSetBlendMode(ctx, .Multiply) CGContextDrawImage(ctx, imageRect, image.CGImage) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } } 
+9
Oct 13
source share

In Swift 3.0

 imageView.image? = (imageView.image?.withRenderingMode(.alwaysTemplate))! imageView.tintColor = UIColor.magenta 

In Swift 2.0

 yourImage.image? = (yourImage.image?.imageWithRenderingMode(.AlwaysTemplate))! yourImage.tintColor = UIColor.magentaColor() 

Enjoy the Swift Pioneers

+7
Jan 08 '16 at 3:37
source share

Swift 4.2 Solution

 extension UIImage { func withColor(_ color: UIColor) -> UIImage { UIGraphicsBeginImageContextWithOptions(size, false, scale) guard let ctx = UIGraphicsGetCurrentContext(), let cgImage = cgImage else { return self } color.setFill() ctx.translateBy(x: 0, y: size.height) ctx.scaleBy(x: 1.0, y: -1.0) ctx.clip(to: CGRect(x: 0, y: 0, width: size.width, height: size.height), mask: cgImage) ctx.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) guard let colored = UIGraphicsGetImageFromCurrentImageContext() else { return self } UIGraphicsEndImageContext() return colored } } // Usage: // let redImage = UIImage().withColor(.red) 
+3
Oct 22 '18 at 2:08
source share

If you don't need to do this programmatically, you can just do it using the Xcode UI.

If you go to the image in the folder with your images, open the inspector on the right side and the "Render As" drop-down menu will appear with the following parameters:

  • Default
  • Original
  • Template

Once you have made your Template selection, you can change the tintColor of the image as you want - regardless of whether it uses the Xcode storyboard user interface or programmatically.

enter image description here

See this image:

enter image description here

+2
Nov 02 '16 at 6:05
source share

Here is my adaptation of @Anna's answer. Two key points here:

  • Use destinationIn Blend Mode
  • Call UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale) to get a smooth image

Code in Swift 3 :

 extension UIImage { static func coloredImage(image: UIImage?, color: UIColor) -> UIImage? { guard let image = image else { return nil } let backgroundSize = image.size UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale) let ctx = UIGraphicsGetCurrentContext()! var backgroundRect=CGRect() backgroundRect.size = backgroundSize backgroundRect.origin.x = 0 backgroundRect.origin.y = 0 var r:CGFloat = 0 var g:CGFloat = 0 var b:CGFloat = 0 var a:CGFloat = 0 color.getRed(&r, green: &g, blue: &b, alpha: &a) ctx.setFillColor(red: r, green: g, blue: b, alpha: a) ctx.fill(backgroundRect) var imageRect = CGRect() imageRect.size = image.size imageRect.origin.x = (backgroundSize.width - image.size.width) / 2 imageRect.origin.y = (backgroundSize.height - image.size.height) / 2 // Unflip the image ctx.translateBy(x: 0, y: backgroundSize.height) ctx.scaleBy(x: 1.0, y: -1.0) ctx.setBlendMode(.destinationIn) ctx.draw(image.cgImage!, in: imageRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } } 
+2
Aug 18 '17 at 10:53 on
source share

The base on @Anna answers and I rewrite for swift 2.2 and processes the image with the alpha channel:

 static func multiplyImageByConstantColor(image:UIImage,color:UIColor)->UIImage{ let backgroundSize = image.size UIGraphicsBeginImageContext(backgroundSize) let ctx = UIGraphicsGetCurrentContext() var backgroundRect=CGRect() backgroundRect.size = backgroundSize backgroundRect.origin.x = 0 backgroundRect.origin.y = 0 var r:CGFloat = 0 var g:CGFloat = 0 var b:CGFloat = 0 var a:CGFloat = 0 color.getRed(&r, green: &g, blue: &b, alpha: &a) CGContextSetRGBFillColor(ctx, r, g, b, a) // Unflip the image CGContextTranslateCTM(ctx, 0, backgroundSize.height) CGContextScaleCTM(ctx, 1.0, -1.0) CGContextClipToMask(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); CGContextFillRect(ctx, backgroundRect) var imageRect=CGRect() imageRect.size = image.size imageRect.origin.x = (backgroundSize.width - image.size.width)/2 imageRect.origin.y = (backgroundSize.height - image.size.height)/2 CGContextSetBlendMode(ctx, .Multiply) CGContextDrawImage(ctx, imageRect, image.CGImage) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage } 
+1
Apr 14 '16 at 10:59
source share

Anna code works well for copying UIImage.image over a colored background using kCGBlendModeNormal rather than kCGBlendModeMultiply. For example, self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor]; will put the contents of mainImage.image in the shade of yourColor, preserving the opacity of your color. This solved my problem of placing a background with opacity for the image to be saved in Camera Roll.

0
May 6 '16 at 3:12
source share

Swift 3.0 version of Anna's great code:

 extension UIImage{ static func multiplyImageByConstantColor(image:UIImage,color:UIColor)-> UIImage { let backgroundSize = image.size UIGraphicsBeginImageContext(backgroundSize) let ctx = UIGraphicsGetCurrentContext()! var backgroundRect=CGRect() backgroundRect.size = backgroundSize backgroundRect.origin.x = 0 backgroundRect.origin.y = 0 let myFloatForR = 0 var r = CGFloat(myFloatForR) let myFloatForG = 0 var g = CGFloat(myFloatForG) let myFloatForB = 0 var b = CGFloat(myFloatForB) let myFloatForA = 0 var a = CGFloat(myFloatForA) color.getRed(&r, green: &g, blue: &b, alpha: &a) ctx.setFillColor(red: r, green: g, blue: b, alpha: a) ctx.fill(backgroundRect) var imageRect=CGRect() imageRect.size = image.size imageRect.origin.x = (backgroundSize.width - image.size.width)/2 imageRect.origin.y = (backgroundSize.height - image.size.height)/2 // Unflip the image ctx.translateBy(x: 0, y: backgroundSize.height) ctx.scaleBy(x: 1.0, y: -1.0) ctx.setBlendMode(.multiply) ctx.draw(image.cgImage!, in: imageRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } } 
0
Sep 18 '16 at 15:20
source share

Swift 3:

 extension UIImage{ static func multiplyImageByConstantColor(image:UIImage,color:UIColor) -> UIImage{ let backgroundSize = image.size UIGraphicsBeginImageContext(backgroundSize) guard let ctx = UIGraphicsGetCurrentContext() else {return image} var backgroundRect=CGRect() backgroundRect.size = backgroundSize backgroundRect.origin.x = 0 backgroundRect.origin.y = 0 var r:CGFloat = 0 var g:CGFloat = 0 var b:CGFloat = 0 var a:CGFloat = 0 color.getRed(&r, green: &g, blue: &b, alpha: &a) ctx.setFillColor(red: r, green: g, blue: b, alpha: a) // Unflip the image ctx.translateBy(x: 0, y: backgroundSize.height) ctx.scaleBy(x: 1.0, y: -1.0) ctx.clip(to: CGRect(0, 0, image.size.width, image.size.height), mask: image.cgImage!) ctx.fill(backgroundRect) var imageRect=CGRect() imageRect.size = image.size imageRect.origin.x = (backgroundSize.width - image.size.width)/2 imageRect.origin.y = (backgroundSize.height - image.size.height)/2 ctx.setBlendMode(.multiply) ctx.draw(image.cgImage!, in: imageRect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage! } } 
0
Mar 30 '17 at 3:57
source share

Starting with iOS 10, you can use UIGraphicsImageRenderer :

 extension UIImage { func colored(_ color: UIColor) -> UIImage { let renderer = UIGraphicsImageRenderer(size: size) return renderer.image { context in color.setFill() self.draw(at: .zero) context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height), blendMode: .sourceAtop) } } } 
0
May 24 '19 at 13:22
source share



All Articles