Changing UIImage color

IphoneObjective CIosUiimageCgcontext

Iphone Problem Overview


I'm trying to change color of UIImage. My code:

-(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 obtained image is not so well as shoud be: bounds pixels of returned image are intermittent and not so smooth as in my first image. How can I resolve this problem?

Iphone Solutions


Solution 1 - Iphone

Since iOS 7, this is the most simple way of doing it.

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 template ( on right bar - Render as) in your assets. Then the color of the image would be the tint color applied. enter image description here

Solution 2 - Iphone

This is pretty much the answer above, but slightly shortened. This only takes the image as a mask and does not actually "multiply" or color the image.

Objective 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()

Solution 3 - Iphone

Another way to tint an image is to simply multiply it by a constant color. Sometimes, this is preferable because it doesn't "lift" the color values in black areas; it keeps the relative intensities in the image the same. Using an overlay as a tint tends to flatten out the contrast.

This is the code I use:

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

Swift 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
    }
}

Solution 4 - Iphone

Starting from 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)
        }
    }

}

Solution 5 - Iphone

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 you Swift pioneers

Solution 6 - Iphone

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)

Solution 7 - Iphone

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

If you go to the image in your image assets folder, open the inspector on the right hand side and there is a "Render As" dropdown with the following options:

  1. Default
  2. Original
  3. Template

Once you've made the Template selection, you can change the tintColor of the image however you want - whether it is using the Xcode storyboard UI or programmatically.

enter image description here

See this image:

enter image description here

Solution 8 - Iphone

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

  • Use destinationIn blending mode
  • Call UIGraphicsBeginImageContextWithOptions(backgroundSize, false, UIScreen.main.scale) to get smooth image

Code in 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!
    }
}

Solution 9 - Iphone

For iOS 13 and newer:

let redImage = image.withTintColor(.red, renderingMode: .alwaysTemplate)

Solution 10 - Iphone

Base on @Anna's answer and I rewrite for swift 2.2 and handles image with 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
}

Solution 11 - Iphone

Anna's code works well for copying a UIImage.image over a colored .image background by using kCGBlendModeNormal rather than kCGBlendModeMultiply. For instance, self.mainImage.image = [self NormalImageByConstantColor: self.mainImage.image withColor: yourColor]; will place the contents of mainImage.image over the tint yourColor while preserving the opacity of yourColor. This solved my problem of placing a background color with opacity behind an image to be saved to the Camera Roll.

Solution 12 - Iphone

Swift 3.0 version of Anna's marvelous 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!
	}
}

Solution 13 - Iphone

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!
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRomanHouseView Question on Stackoverflow
Solution 1 - IphoneAnkish JainView Answer on Stackoverflow
Solution 2 - Iphoneuser1270061View Answer on Stackoverflow
Solution 3 - IphoneAnna DickinsonView Answer on Stackoverflow
Solution 4 - IphoneMurlakatamView Answer on Stackoverflow
Solution 5 - IphoneChris KlinglerView Answer on Stackoverflow
Solution 6 - IphoneCharlton ProvatasView Answer on Stackoverflow
Solution 7 - IphoneMario A GuzmanView Answer on Stackoverflow
Solution 8 - IphonealgridView Answer on Stackoverflow
Solution 9 - IphoneberylliumView Answer on Stackoverflow
Solution 10 - IphoneBill ChanView Answer on Stackoverflow
Solution 11 - IphoneLowell BahnerView Answer on Stackoverflow
Solution 12 - IphoneMatthieuView Answer on Stackoverflow
Solution 13 - IphoneNursultan AskarbekulyView Answer on Stackoverflow