Question

I'm attempting to add a black overlay over some current UIImage's (which are white). I've been trying to use following code:

[[UIColor blackColor] set];
[image drawAtPoint:CGPointMake(0, 0) blendMode:kCGBlendModeOverlay alpha:1.0];

But it's not working, and I'm pretty sure set isn't supposed to be there.

Was it helpful?

Solution

You will want to clip the context to an image mask and then fill with a solid color:

- (void)drawRect:(CGRect)rect
{
    CGRect bounds = [self bounds];
    [[UIColor blackColor] set];
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClipToMask(context, bounds, [myImage CGImage]);
    CGContextFillRect(context, bounds);
}

Note: myImage should be an instance variable that contains an UIImage. I'm not sure whether it takes the mask from the alpha channel or the intensity so try both.

OTHER TIPS

So, to sum up all the answers into one here's the drop-in method that works perfectly from iOS 6 all the way up to iOS 11 with all kinds of images and icons:

+ (UIImage *)filledImageFrom:(UIImage *)source withColor:(UIColor *)color{

    // begin a new image context, to draw our colored image onto with the right scale
    UIGraphicsBeginImageContextWithOptions(source.size, NO, [UIScreen mainScreen].scale);

    // get a reference to that context we created
    CGContextRef context = UIGraphicsGetCurrentContext();

    // set the fill color
    [color setFill];

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

    CGContextSetBlendMode(context, kCGBlendModeColorBurn);
    CGRect rect = CGRectMake(0, 0, source.size.width, source.size.height);
    CGContextDrawImage(context, rect, source.CGImage);

    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    CGContextAddRect(context, rect);
    CGContextDrawPath(context,kCGPathFill);

    // generate a new UIImage from the graphics context we drew onto
    UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    //return the color-burned image
    return coloredImg;
}

Update: Swift 3 version

func filledImage(source: UIImage, fillColor: UIColor) -> UIImage {

    UIGraphicsBeginImageContextWithOptions(source.size, false, UIScreen.main.scale)

    let context = UIGraphicsGetCurrentContext()
    fillColor.setFill()

    context!.translateBy(x: 0, y: source.size.height)
    context!.scaleBy(x: 1.0, y: -1.0)

    context!.setBlendMode(CGBlendMode.colorBurn)
    let rect = CGRect(x: 0, y: 0, width: source.size.width, height: source.size.height)
    context!.draw(source.cgImage!, in: rect)

    context!.setBlendMode(CGBlendMode.sourceIn)
    context!.addRect(rect)
    context!.drawPath(using: CGPathDrawingMode.fill)

    let coloredImg : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
    UIGraphicsEndImageContext()

    return coloredImg
}

I just wrote a tutorial that will help with this. My approach gives you a copy of a UIImage, with the color alterations that you want. rpetrich's approach is great, but requires that you're creating a subclass. My approach is just a few lines of code that can be dropped in wherever you need them. http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

NSString *name = @"badge.png";
UIImage *img = [UIImage imageNamed:name];

// begin a new image context, to draw our colored image onto
UIGraphicsBeginImageContext(img.size);

// get a reference to that context we created
CGContextRef context = UIGraphicsGetCurrentContext();

// set the fill color
[color setFill];

// 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);

// set the blend mode to color burn, and the original image
CGContextSetBlendMode(context, kCGBlendModeColorBurn);
CGRect rect = CGRectMake(0, 0, img.size.width, img.size.height);
CGContextDrawImage(context, rect, img.CGImage);

// set a mask that matches the shape of the image, then draw (color burn) a colored rectangle
CGContextClipToMask(context, rect, img.CGImage);
CGContextAddRect(context, rect);
CGContextDrawPath(context,kCGPathFill);

// generate a new UIImage from the graphics context we drew onto
UIImage *coloredImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

//return the color-burned image
return coloredImg;

Since iOS 7 there is a much simpler solution:

UIImage* im = [UIImage imageNamed:@"blah"];
im = [im imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[[UIColor blackColor] setFill];
[im drawInRect:rect];

Look on this method

 + (UIImage *)imageWithColor:(UIColor *)color andSize:(CGSize)size;
    {
      UIImage *img = nil;

      CGRect rect = CGRectMake(0, 0, size.width, size.height);
      UIGraphicsBeginImageContext(rect.size);
      CGContextRef context = UIGraphicsGetCurrentContext();
      CGContextSetFillColorWithColor(context,
                                     color.CGColor);
      CGContextFillRect(context, rect);
      img = UIGraphicsGetImageFromCurrentImageContext();

      UIGraphicsEndImageContext();

      return img;
    }

In addition to the solution by rpetrich (which is great by the way - help me superbly), you can also replace the CGContextClipToMask line with:

    CGContextSetBlendMode(context, kCGBlendModeSourceIn); //this is the main bit!

It's the SourceIn blendmode that does the job of masking the color by whatever is in the GetCurrentContext.

Here's how you do it with Swift 3.0 and using extensions to UIImage. Super simple.

public extension UIImage {
    func filledImage(fillColor: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)

        let context = UIGraphicsGetCurrentContext()!
        fillColor.setFill()

        context.translateBy(x: 0, y: self.size.height)
        context.scaleBy(x: 1.0, y: -1.0)

        context.setBlendMode(CGBlendMode.colorBurn)

        let rect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
        context.draw(self.cgImage!, in: rect)

        context.setBlendMode(CGBlendMode.sourceIn)
        context.addRect(rect)
        context.drawPath(using: CGPathDrawingMode.fill)

        let coloredImg : UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return coloredImg
    }
}

Then to run it simply do:

let image = UIImage(named: "image_name").filledImage(fillColor: UIColor.red)

-set is used to set the colour of subsequent drawing operations which doesn't include blits. I suggest as a first call, displaying another (empty) UIView over your UIImageView and setting its background colour:

myView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];

Obviously you should use the white and alpha values you want.

You can do that programmatically in Swift 4.x on the next way:

imageView.image = UIImage(named: "imageName")?.withRenderingMode(.alwaysTemplate)
imageView.tintColor = .purple
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top