35

I would love to know how to color an image (make a white .png red, for example). I have seen various suggestions but never any confirmation that this is actually possible. I have tried this:

-(UIImage *)colorizeImage:(UIImage *)baseImage color:(UIColor *)theColor {
    UIGraphicsBeginImageContext(baseImage.size);

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect area = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);

    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -area.size.height);
    CGContextSaveGState(ctx);
    CGContextClipToMask(ctx, area, baseImage.CGImage);
    [theColor set];
    CGContextFillRect(ctx, area);
    CGContextRestoreGState(ctx);
    CGContextSetBlendMode(ctx, kCGBlendModeNormal);
    CGContextDrawImage(ctx, area, baseImage.CGImage);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

myImageView.image = [self colorizeImage:[UIImage imageNamed:@"whiteImage.png"] color:[UIColor redColor]];

But it doesn't work - the image is still white on the screen.

  • But what exactly are you trying to do? Are you trying to turn all the pixels that are completely white (RGB(255,255,255)) to red? Do you just want to add a red tint? Is your "whiteImage.png" actually just a big rectangle of all white? – Amagrammer Aug 03 '09 at 16:56
  • Yes, I want to turn the white (or gray) pixels into red. The transparent part of the png should remain transparent. whiteImage.png is not a white rectangle - it is an image of an animal, for example. –  Aug 03 '09 at 17:15
  • Is there a particular reason that you can't just have a red version of the same image, and toggle between them as needed? Bi-color images compress very small. – Amagrammer Aug 03 '09 at 17:56
  • Can refer http://stackoverflow.com/questions/19274789/how-can-i-change-image-tintcolor-in-ios-and-watchkit/41149046#41149046 – Shrawan Dec 14 '16 at 17:55

10 Answers10

64

Make this a UIImage category. It also takes into account the scale factor with iOS 4.

- (UIImage *)imageWithOverlayColor:(UIColor *)color
{        
    CGRect rect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height);

    if (UIGraphicsBeginImageContextWithOptions) {
        CGFloat imageScale = 1.0f;
        if ([self respondsToSelector:@selector(scale)])  // The scale property is new with iOS4.
            imageScale = self.scale;
        UIGraphicsBeginImageContextWithOptions(self.size, NO, imageScale);
    }
    else {
        UIGraphicsBeginImageContext(self.size);
    }

    [self drawInRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);

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

    return image;
}

UPDATE - Swift version:

func imageWithColor(color: UIColor) -> UIImage {
    let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
    UIGraphicsBeginImageContextWithOptions(self.size, false, 0.0);
    drawInRect(rect)
    let context = UIGraphicsGetCurrentContext()
    CGContextSetBlendMode(context, .SourceIn)
    CGContextSetFillColorWithColor(context, color.CGColor)
    CGContextFillRect(context, rect);
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image
}
Dave Batton
  • 8,795
  • 1
  • 46
  • 50
  • Thanks for this... exactly what I needed to colorize the background of an interface feature, based on the users chosen theme. – Jim Rhoades Mar 23 '11 at 14:44
  • 1
    +1 Thanks it worked for me. Before I was getting only white color, now {CGContextSetBlendMode(ctx, kCGBlendModeMultiply);} as told by willc2 its working fine. – HDdeveloper Jan 27 '13 at 11:33
  • One morning to find this... And that was so simple! I love you Dave Balton. – Martin Feb 01 '13 at 11:44
  • This is great, by far the best solution for accomplishing the coloring of UIImages. I spent the entire day looking for a way to recolor a UIImage and also be able to use the `resizableImageWithCapInsets` method. `[[[UIImage imageNamed:@"myImage.png"] imageWithOverlayColor:[UIColor blackColor]] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 15)];` – davis Jun 24 '13 at 04:57
  • worked for graphics with clear color backgrounds where you only want to color something that is non-clear. – kraftydevil Nov 04 '14 at 07:15
  • 2
    There's a simpler solution for this in iOS7+ see http://stackoverflow.com/a/27356928/3624478 – Magoo Feb 26 '15 at 11:51
12

Change your blend mode to multiply and your code will work:

CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
willc2
  • 38,991
  • 25
  • 88
  • 99
10

As of iOS 7 you can tint images with a single colour like so

Objective C

UIImage *image = [UIImage imageNamed:@"myImage.png"];
UIImageView *imageView = [[UIImageView alloc]initWithImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
imageView.tintColor = [UIColor redColor];

Swift 3

let image = UIImage(named:"myImage.png")?.withRenderingMode(.alwaysTemplate)
let imageView = UIImageView(image:image)
imageView.tintColor = .red

This will colour an entire image with a single colour and preserve the alpha channel.

Magoo
  • 2,552
  • 1
  • 23
  • 43
2

I think that the best way to colorize an image from iOS7 is by specifying the property UIImageRenderingModeAlwaysTemplate on your image:

myImageView.image = [[UIImage imageNamed:@"myImage"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];

and then colorize it with the tintColor of the imageView

myImageView.tintColor = [UIColor purpleColor]; //Maybe purple is not the best choice ;)

I think it's easier (no category needed) and of course optimized!

Note: if you are using xcassets, you can set the rendering property to UIImageRenderingModeAlwaysTemplate in XCode, like in this screenshot:

UIImageRenderingModeAlwaysTemplate in XCode

andreacipriani
  • 2,519
  • 26
  • 23
1

Okay, how's this?

In your asset creation phase (NOT dynamically while the application is running), invert the color scheme of your images. Part that is now white -> transparent. Part that is now transparent -> whatever the background of the page is.

Under each image, place a blank white view of the same size. When you wish to turn the image to red, change the color of that blank white view to red.

Is that doable?

Amagrammer
  • 6,385
  • 3
  • 28
  • 30
1

Short, sweet and better :)

- (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color

Define UIColorFromRGB to use hex color codes then just call colorizeImage:withColor

#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIImage *imgToColor = [UIImage imageNamed:@"myImage.png"];
    imgToColor = [self colorizeImage:imgToColor withColor:UIColorFromRGB(0xff00ff)];
    // use normal UIColor or UIColorFromRGB() for hex
}

 - (UIImage *)colorizeImage:(UIImage *)image withColor:(UIColor *)color
{
    CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
    UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
    [image drawInRect:rect];

    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);

    CGContextSetFillColorWithColor(context, color.CGColor);
    CGContextFillRect(context, rect);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
emotality
  • 12,795
  • 4
  • 39
  • 60
1

@geek kid, let me know if this helps u dude...

// translate/flip the graphics context (for transforming from CG* coords to UI* coords)
CGContextTranslateCTM(context, 0, img.size.height);
CGContextScaleCTM(context, 1.0, -1.0);

//replace "context" with UIGraphicsGetCurrentContext() or if u have an instance of the current context.

Hope that helps.

Maulik
  • 19,348
  • 14
  • 82
  • 137
Ram
  • 11
  • 2
0

If you set the backgroundColor of an UIImageView to [UIColor redColor], the parts of the original PNG that were transparent will become red. Alternatively, you could try adding a semi-transparent UIView with background color red as subview of the imageview. This will add a red haze to the view displaying the image.

drvdijk
  • 5,556
  • 2
  • 29
  • 48
  • 1
    Thanks, but I want to color the pixels that are present only, not the entire rectangle. –  Aug 03 '09 at 17:16
0

You're drawing the color first, and then drawing the image on top of it. Unless the image is partially transparent, it's going to completely overdraw the background color. I recommend trying to draw the original image first, and then drawing red over it using kCGBlendModeHue.

I'm not certain why you're flipping and translating your context. Is your picture upside down?

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

If you want hue change (like what Rob Napier suggested), not just a tinted image, use this UIImage extension:

extension UIImage {
    func colorized(color: UIColor) -> UIImage? {
        defer {
            UIGraphicsEndImageContext()
        }

        let frame = CGRect(origin: .zero, size: size)

        UIGraphicsBeginImageContext(size)

        guard let context = UIGraphicsGetCurrentContext() else {
            return nil
        }

        context.setFillColor(color.cgColor)
        context.fill(frame)

        guard let solidColorImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }

        context.clear(frame)

        draw(in: frame)
        solidColorImage.draw(in: frame, blendMode: .hue, alpha: 1)

        guard let colorizedImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }

        return colorizedImage
    }
}

Result

original images colorized images

Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223