7

I am working on an app - or rather on some re-usable "framework" which I am happy to share once it works. Within this app the user should be able to choose from a list of color themes. Therefore the app must be able to tint its UI elements in some rather dynamic way.

For Buttons all the tinting does not work. Properly tinted background images must be supplied here. But preparing one set of background images for each them is just second best. It is not dynamic and flexible enough.

In the end a solution may come down to providing one monochrome (grey) gradiented image for the selected and normal state and tint that image programmatically using CoreGraphics or OpenGL. But frankly, I do not know where to start there. How should the gradient look like and how would I then programmatically tint that in any given color?

Pretty much applies to UISegmentedControls, just a bit more complicated. :) Any generic solution that covers UISegementedControls too is highliy appreciated.

Brian
  • 14,610
  • 7
  • 35
  • 43
Hermann Klecker
  • 14,039
  • 5
  • 48
  • 71
  • Take a look at [GMButton](https://github.com/progrmr/SDK_Utilities/blob/master/GM_Subclasses/GMButton.h). It allows you to set colors for the all the button states and provides bevel and gloss gradients as well. – progrmr Jan 24 '13 at 22:55
  • Looks good what you did there. It seems that I just need to adopt this to ARC and then it should do everything I want. Thanks. – Hermann Klecker Jan 24 '13 at 23:04

6 Answers6

42

In iOS7 you can use the method imagedWithRenderingMode: in conjunction with the method setTintColor:

But, be sure to use UIImageRenderingModeAlwaysTemplate as it replaces all non-transparent colors on a UIImage with the tint color. The default is UIImageRenderingModeAutomatic.

UIImageRenderingModeAlwaysTemplate

Always draw the image as a template image, ignoring its color information.

Objective-C:

UIImage * image = [myButton.currentImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[myButton setImage:image forState:UIControlStateNormal];

Swift:

let image = myButton.currentImage?.withRenderingMode(.alwaysTemplate)
myButton.setImage(image, for: .normal)
Jonathan
  • 2,623
  • 3
  • 23
  • 38
12

I made a UIImage category following another post that i cant find that takes the image and tints it as follows:

- (UIImage *)tintedImageWithColor:(UIColor *)tintColor {
    UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef context = UIGraphicsGetCurrentContext();


    CGContextTranslateCTM(context, 0, self.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

    // draw alpha-mask
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, self.CGImage);

    // draw tint color, preserving alpha values of original image
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return coloredImage;
}

This will take the image and fill in all of the areas with an alpha value with the given color.

Simon Adcock
  • 3,554
  • 3
  • 25
  • 41
horsejockey
  • 817
  • 10
  • 18
4

I created a UIButton category using horsejockey's code: https://github.com/filipstefansson/UITintedButton

Here's the new methods:

-(void)setImageTintColor:(UIColor *)color;
-(void)setBackgroundTintColor:(UIColor *)color forState:(UIControlState)state;
+(void)tintButtonImages:(NSArray *)buttons withColor:(UIColor *)color;
+(void)tintButtonBackgrounds:(NSArray *)buttons withColor:(UIColor *)color forState:(UIControlState)state;
Filip Stefansson
  • 211
  • 1
  • 14
1

Here is a partial answer to your question. This is a helper method I wrote that tints a grey scale image:

// baseImage is the grey scale image. color is the desired tint color
+ (UIImage *)tintImage:(UIImage *)baseImage withColor:(UIColor *)color {
    UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, baseImage.scale);

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

    [color set];
    CGContextFillRect(ctx, area);
    CGContextRestoreGState(ctx);

    CGContextSetBlendMode(ctx, kCGBlendModeOverlay);

    CGContextDrawImage(ctx, area, baseImage.CGImage);

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    // If the original image was stretchable, make the new image stretchable
    if (baseImage.leftCapWidth || baseImage.topCapHeight) {
        newImage = [newImage stretchableImageWithLeftCapWidth:baseImage.leftCapWidth topCapHeight:baseImage.topCapHeight];
    }

    return newImage;
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks so far. I'll give it a try over the weekend or early next week. It looks petty similar to the core of what @OnlyYou has linked. Although I am not quite familiar with CG Graphics jet it is quite self explaining. Just one question. This CGContextScalTCM and ContextTranslateTCM is for the coordinate transformation because UIKit and CGGraphics have different origins and a different orientation on the y-axix? – Hermann Klecker Jan 25 '13 at 10:46
0

I'm refering you to a SO question that seems to be what you are looking for and it's got an answer.

How to tint a transparent PNG image in iPhone?

Şafak Gezer
  • 3,928
  • 3
  • 47
  • 49
Only You
  • 2,051
  • 1
  • 21
  • 34
0

I modifie some things from "horsejockey":

+(UIImage *)getTintedImage:(UIImage*)image withColor:(UIColor *)tintColor
{
    UIGraphicsBeginImageContextWithOptions(image.size, NO, [[UIScreen mainScreen] scale]);
    CGContextRef context = UIGraphicsGetCurrentContext();


    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);

    // draw alpha-mask
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGContextDrawImage(context, rect, image.CGImage);

    // draw tint color, preserving alpha values of original image
    CGContextSetBlendMode(context, kCGBlendModeSourceIn);
    [tintColor setFill];
    CGContextFillRect(context, rect);

    UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return coloredImage;
}
Hunt3rDe
  • 482
  • 1
  • 6
  • 9