5

2 things i want to do, which are related:

  1. Show a block of any colour. So i could change that colour to something else at any time.
  2. Tint a UIImage to be a different colour. An overlay of colour with alpha turned down could work here, but say it was an image which had a transparent background and didn't take up the full square of the image.

Any ideas?

Andrew
  • 15,935
  • 28
  • 121
  • 203

5 Answers5

11

Another option, would be to use category methods on UIImage like this...

// Tint the image, default to half transparency if given an opaque colour.
- (UIImage *)imageWithTint:(UIColor *)tintColor {
    CGFloat white, alpha;
    [tintColor getWhite:&white alpha:&alpha];
    return [self imageWithTint:tintColor alpha:(alpha == 1.0 ? 0.5f : alpha)];
}

// Tint the image
- (UIImage *)imageWithTint:(UIColor *)tintColor alpha:(CGFloat)alpha {

    // Begin drawing
    CGRect aRect = CGRectMake(0.f, 0.f, self.size.width, self.size.height);
    UIGraphicsBeginImageContext(aRect.size);

    // Get the graphic context
    CGContextRef c = UIGraphicsGetCurrentContext(); 

    // Converting a UIImage to a CGImage flips the image, 
    // so apply a upside-down translation
    CGContextTranslateCTM(c, 0, self.size.height); 
    CGContextScaleCTM(c, 1.0, -1.0);

    // Draw the image
    [self drawInRect:aRect];

    // Set the fill color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextSetFillColorSpace(c, colorSpace);

    // Set the mask to only tint non-transparent pixels
    CGContextClipToMask(c, aRect, self.CGImage);

    // Set the fill color
    CGContextSetFillColorWithColor(c, [tintColor colorWithAlphaComponent:alpha].CGColor);

    UIRectFillUsingBlendMode(aRect, kCGBlendModeColor);

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

    // Release memory
    CGColorSpaceRelease(colorSpace);

    return img;
}
Jon Willis
  • 6,993
  • 4
  • 43
  • 51
Daniel Thorpe
  • 3,911
  • 2
  • 28
  • 28
  • 2
    1) To fix "transparent pixels tinted too," add `CGContextClipToMask(c, aRect, self.CGImage);` before `UIRectFillUsingBlendMode`. 2) To apply a true tint, preserving image values, use kCGBlendModeColor. – Wienke Jan 03 '13 at 18:09
  • 1
    This renders the image flippes upside-down. After getting the context, the following lines need to be added: `CGContextTranslateCTM(c, 0, self.size.height); CGContextScaleCTM(c, 1.0, -1.0);` – chitza Jan 16 '13 at 19:18
  • @chitza Or just remove the two lines that apply the upside-down transformation in the first place – Carlos P Dec 01 '14 at 16:36
10

The first one is easy. Make a new UIView and set its background color to whatever color you’d like.

The second is more difficult. As you mentioned, you can put a new view on top of it with transparency turned down, but to get it to clip in the same places, you’d want to use a mask. Something like this:

UIImage *myImage = [UIImage imageNamed:@"foo.png"];

UIImageView *originalImageView = [[UIImageView alloc] initWithImage:myImage];
[originalImageView setFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[parentView addSubview:originalImageView];

UIView *overlay = [[UIView alloc] initWithFrame:[originalImageView frame]];

UIImageView *maskImageView = [[UIImageView alloc] initWithImage:myImage];
[maskImageView setFrame:[overlay bounds]];

[[overlay layer] setMask:[maskImageView layer]];

[overlay setBackgroundColor:[UIColor redColor]];

[parentView addSubview:overlay];

Keep in mind you’ll have to #import <QuartzCore/QuartzCore.h> in the implementation file.

Jeff Kelley
  • 19,021
  • 6
  • 70
  • 80
  • I had some difficulty getting horizontal centering to work. At first, I was adding overlay as a subview of a UITableViewCell, and centering was lost when the cell resized. I fixed this by adding the overlay as a subview of the UILabel field within the cell, instead of the UITableViewCell itself. – bneely May 08 '12 at 22:43
  • 1
    You can also try adding it to the `contentView` of the table view cell. – Jeff Kelley May 10 '12 at 15:12
3

Here is another way to implement image tinting, especially if you are already using QuartzCore for something else.

Import QuartzCore:

#import <QuartzCore/QuartzCore.h>    

Create transparent CALayer and add it as a sublayer for the image you want to tint:

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

Add QuartzCore to your projects Framework list (if it isn't already there), otherwise you'll get compiler errors like this:

Undefined symbols for architecture i386: "_OBJC_CLASS_$_CALayer"
lekksi
  • 4,868
  • 1
  • 16
  • 13
2

An easy way to achieve 1 is to create a UILabel or even a UIView and change the backgroundColor as you like.

There is a way to multiply colours instead of just overlaying them, and that should work for 2. See this tutorial.

PengOne
  • 48,188
  • 17
  • 130
  • 149
-1

Try this

   - (void)viewDidLoad
    {
        [super viewDidLoad];
        UIView* maskedView = [self filledViewForPNG:[UIImage imageNamed:@"mask_effect.png"]
                                               mask:[UIImage imageNamed:@"mask_image.png"]
                                          maskColor:[UIColor colorWithRed:.6 green:.2 blue:.7 alpha:1]];


        [self.view addSubview:maskedView];

    }

    -(UIView*)filledViewForPNG:(UIImage*)image mask:(UIImage*)maskImage maskColor:(UIColor*)maskColor
    {
        UIImageView *pngImageView = [[UIImageView alloc] initWithImage:image];
        UIImageView *maskImageView = [[UIImageView alloc] initWithImage:maskImage];

        CGRect bounds;
        if (image) {
            bounds = pngImageView.bounds;
        }
        else
        {
            bounds = maskImageView.bounds;
        }
        UIView* parentView = [[UIView alloc]initWithFrame:bounds];
        [parentView setAutoresizesSubviews:YES];
        [parentView setClipsToBounds:YES];

        UIView *overlay = [[UIView alloc] initWithFrame:bounds];
        [[overlay layer] setMask:[maskImageView layer]];
        [overlay setBackgroundColor:maskColor];

        [parentView addSubview:overlay];
        [parentView addSubview:pngImageView];
        return parentView;
    }