Color Tint UIButton Image
Asked Answered
W

22

343

I noticed that when I place a white or black UIImage into a UISegmentedControl it automatically color masks it to match the tint of the segmented control. I thought this was really cool, and was wondering if I could do this elsewhere as well. For example, I have a bunch of buttons that have a uniform shape but varied colors. Instead of making a PNG for each button, could I somehow use this color masking to use the same image for all of them but then set a tint color or something to change their actual color?

Wikiup answered 7/11, 2013 at 6:26 Comment(2)
Can you post the image that you want to use and also image for desired result?Proudhon
this do the same from interface builder https://mcmap.net/q/94328/-tint-uibutton-39-s-image-from-interface-builder-with-uiimagerenderingmodealwaystemplateAchieve
F
708

As of iOS 7, there is a new method on UIImage to specify the rendering mode. Using the rendering mode UIImageRenderingModeAlwaysTemplate will allow the image color to be controlled by the button's tint color.

Objective-C

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *image = [[UIImage imageNamed:@"image_name"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[button setImage:image forState:UIControlStateNormal]; 
button.tintColor = [UIColor redColor];

Swift

let button = UIButton(type: .custom)
let image = UIImage(named: "image_name")?.withRenderingMode(.alwaysTemplate)
button.setImage(image, for: .normal)
button.tintColor = UIColor.red
Fumy answered 27/7, 2014 at 9:21 Comment(8)
I think its better to use button.imageView.tintColorHeyday
@Heyday only works for me when I use button.tintColor and not button.imageview.tintColorDominions
Just remember to set image render mode on the image in xcassets per @hashierStilly
Creating a UIButton of type UIButtonTypeSystem will automatically honor the tintColor you set.Enucleate
If your tintColor is too dark, make sure adjustsImageWhenHighlighted is NO. (Or in IB: "Highlighted Adjusts Image" is unchecked.)Groove
@JasonMoore what an awesome comment! You just made my day, I didn't know why my UIButton was changing its tintColor and it was that! Thank youImpanation
It is working for setImage for button but not for background image. And your efforts rewarded in advancePreexist
Just realized my image is actually an emoji... this is not going to workFluorometer
A
245

As Ric already mentioned in his post you can set the render mode in code, you can also do this directly in the image catalog, see attached image below. Just set the Render As to Template Image

enter image description here

Caveat I have had problems with iOS 7 and this approach. So if you use iOS 7 as well you might want to do it in code as well to be sure, as described here.

Accipitrine answered 25/11, 2014 at 9:10 Comment(2)
As a side note, the image file should be black and transparent (not b&w)Ruwenzori
@AxelGuilmin not really. Your image can be any color you want and transparent. Any color different than transparent will be converted to a generic one and then it will be tinted with black color by default.Impanation
P
105

Custom Buttons appear in their respective image colors. Setting the button type to "System" in the storyboard (or to UIButtonTypeSystem in code), will render the button's image with the default tint color.

Button Type System Renders Icons tinted

(tested on iOS9, Xcode 7.3)

Piggott answered 25/6, 2016 at 16:49 Comment(1)
Which can be avoided with the alwaysTemplate and tintColor methods suggested here. With the advantage of the .system button being you get the change in color with touch down on the tap.Grindery
S
57

You must set the image rendering mode to UIImageRenderingModeAlwaysTemplate in order to have the tintColor affect the UIImage. Here is the solution in Swift:

let image = UIImage(named: "image-name")
let button = UIButton()
button.setImage(image?.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate), forState: .Normal)
button.tintColor = UIColor.whiteColor()

SWIFT 4x

button.setImage(image.withRenderingMode(UIImage.RenderingMode.alwaysTemplate), for: .normal)
button.tintColor = UIColor.blue
Shanda answered 5/12, 2014 at 7:50 Comment(0)
A
37

If you have a custom button with a background image.You can set the tint color of your button and override the image with following .

In assets select the button background you want to set tint color.

In the attribute inspector of the image set the value render as to "Template Image"

enter image description here

Now whenever you setbutton.tintColor = UIColor.red you button will be shown in red.

Appertain answered 13/4, 2017 at 13:26 Comment(3)
if you are here you can go to this link: useyourloaf.com/blog/styling-buttons-using-the-asset-catalog and go down to template images (to serve as an explanation)Handset
This is a better solution as it reduces code and pretty much does the same thing as the accepted answer.Caplan
I think this should be the correct one. less code and easy to handle.Ovovitellin
S
18

In Swift you can do that like so:

var exampleImage = UIImage(named: "ExampleImage.png")?.imageWithRenderingMode(.AlwaysTemplate)

Then in your viewDidLoad

exampleButtonOutlet.setImage(exampleImage, forState: UIControlState.Normal)

And to modify the color

exampleButtonOutlet.tintColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1) //your color

EDIT Xcode 8 Now you can also just the rendering mode of the image in your .xcassets to Template Image and then you don't need to specifically declare it in the var exampleImage anymore

Supernaturalism answered 10/5, 2015 at 12:23 Comment(0)
H
14

Not sure exactly what you want but this category method will mask a UIImage with a specified color so you can have a single image and change its color to whatever you want.

ImageUtils.h

- (UIImage *) maskWithColor:(UIColor *)color;

ImageUtils.m

-(UIImage *) maskWithColor:(UIColor *)color 
{
    CGImageRef maskImage = self.CGImage;
    CGFloat width = self.size.width;
    CGFloat height = self.size.height;
    CGRect bounds = CGRectMake(0,0,width,height);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);
    CGContextClipToMask(bitmapContext, bounds, maskImage);
    CGContextSetFillColorWithColor(bitmapContext, color.CGColor);    
    CGContextFillRect(bitmapContext, bounds);

    CGImageRef cImage = CGBitmapContextCreateImage(bitmapContext);
    UIImage *coloredImage = [UIImage imageWithCGImage:cImage];

    CGContextRelease(bitmapContext);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cImage);

    return coloredImage;    
}

Import the ImageUtils category and do something like this...

#import "ImageUtils.h"

...

UIImage *icon = [UIImage imageNamed:ICON_IMAGE];

UIImage *redIcon = [icon maskWithColor:UIColor.redColor];
UIImage *blueIcon = [icon maskWithColor:UIColor.blueColor];
Howund answered 7/11, 2013 at 6:42 Comment(4)
Use kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast on CGBitmapContextCreateFinegrained
Nice, but needs to be tweaked for Retina graphics... When I run this on a Retina image, it returns me a non-retina one.Sewel
To support Retina devices you could use the scale property of the UIDevice class: CGFloat scale = [UIScreen mainScreen].scale; CGFloat width = self.size.width * scale; CGFloat height = self.size.height * scale; The scale property exists as of iOS 4.0.Garlandgarlanda
You also need to use the scale value when the UIImage is created: UIImage *coloredImage = [UIImage imageWithCGImage:cImage scale:scale orientation:self.imageOrientation];Garlandgarlanda
L
8

Swift 4 with customType:

let button = UIButton(frame: aRectHere)
    let buttonImage = UIImage(named: "imageName")
    button.setImage(buttonImage?.withRenderingMode(.alwaysTemplate), for: .normal)
    button.tintColor = .white
Lumbricoid answered 11/10, 2016 at 20:28 Comment(0)
E
7

Swift 3:

This solution could be comfortable if you have already setted your image through xCode interface builder. Basically you have one extension to colorize an image:

extension UIImage {
    public func image(withTintColor color: UIColor) -> UIImage{
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        let context: CGContext = UIGraphicsGetCurrentContext()!
        context.translateBy(x: 0, y: self.size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        context.setBlendMode(CGBlendMode.normal)
        let rect: CGRect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
        context.clip(to: rect, mask: self.cgImage!)
        color.setFill()
        context.fill(rect)
        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }
}

Then , you can prepare this UIButton extension to colorize the image for a particular state:

extension UIButton {
    func imageWith(color:UIColor, for: UIControlState) {
        if let imageForState = self.image(for: state) {
            self.image(for: .normal)?.withRenderingMode(.alwaysTemplate)
            let colorizedImage = imageForState.image(withTintColor: color)
            self.setImage(colorizedImage, for: state)
        }
    }
}

Usage:

myButton.imageWith(.red, for: .normal)

P.S. (working good also in table cells, you don't need to call setNeedDisplay() method, the change of the color is immediate due to the UIImage extension..

Endblown answered 15/7, 2017 at 10:34 Comment(0)
C
6

For Xamarin.iOS (C#):

UIButton messagesButton = new UIButton(UIButtonType.Custom);
UIImage icon = UIImage.FromBundle("Images/icon.png");
messagesButton.SetImage(icon.ImageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate), UIControlState.Normal);
messagesButton.TintColor = UIColor.White;
messagesButton.Frame = new RectangleF(0, 0, 25, 25);
Corticate answered 25/10, 2016 at 7:39 Comment(0)
B
6

If you're arriving here after iOS 15 and you're using the new UIButton.Configuration APIs, then you might need to do it via the imageColorTransformer.

Looks like this:

configuration.imageColorTransformer = UIConfigurationColorTransformer { _ in .green }

For convenience, you can create an extension:

extension UIButton.Configuration {
    func imageColor(_ color: UIColor) -> UIButton.Configuration {
        var configuration = self
        configuration.imageColorTransformer = UIConfigurationColorTransformer { _ in color }
        return configuration
    }
}

// Usage:
configuration = configuration.imageColor(.green)

As with the other answers, the image itself has to be "Render As - Template Image" in the Xcode Assets, or in code image.withRenderingMode(.alwaysTemplate)


BONUS TIP:
What if you want the image color to change when the button is highlighted? Then your configuration extension can look like this:

func imageColor(whenNormal: UIColor,
                whenHighlighted: UIColor,
                isHighlighted: Bool) -> UIButton.Configuration {
    var configuration = self
    configuration.imageColorTransformer = UIConfigurationColorTransformer { _ in
        isHighlighted ? whenHighlighted : whenNormal
    }
    return configuration
}

And this itself has to be called from a configurationUpdateHandler context, like this:

someButton.configurationUpdateHandler = { button in
    guard var configuration = button.configuration else {  return }
    configuration.image = UIImage(named: "some_image")
    configuration = configuration.imageColor(whenNormal: .green,
                                             whenHighlighted: .green.withAlphaComponent(0.7),
                                             isHighlighted: button.isHighlighted)
    button.configuration = configuration
}

Note that the configurationUpdateHandler is also where you can actually define a different image based on button state(s).

Bendicty answered 8/11, 2022 at 23:2 Comment(2)
thanks a lot ! the correct and only answer nowHoldover
This answer should be way up to the top. Specifically the detail about "Render As - Template Image" eluded me.Expend
N
5
let button = UIButton(type: .custom)
let image = UIImage(named: "image_name")?.withRenderingMode(.alwaysTemplate)
button.setImage(image, for: .normal)
button.tintColor = UIColor.red

If you are setting UIButton.tintColor by UIColor(r:g:b:alpha:), remember to divide values by 255. Those RGB values should be in between 0 and 1.

Nanny answered 8/4, 2021 at 13:23 Comment(0)
B
3

If you want to manually mask your image, here is updated code that works with retina screens

- (UIImage *)maskWithColor:(UIColor *)color
{
    CGImageRef maskImage = self.CGImage;
    CGFloat width = self.size.width * self.scale;
    CGFloat height = self.size.height * self.scale;
    CGRect bounds = CGRectMake(0,0,width,height);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);
    CGContextClipToMask(bitmapContext, bounds, maskImage);
    CGContextSetFillColorWithColor(bitmapContext, color.CGColor);
    CGContextFillRect(bitmapContext, bounds);

    CGImageRef cImage = CGBitmapContextCreateImage(bitmapContext);
    UIImage *coloredImage = [UIImage imageWithCGImage:cImage scale:self.scale orientation:self.imageOrientation];

    CGContextRelease(bitmapContext);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cImage);

    return coloredImage;
}
Bendite answered 10/8, 2015 at 5:40 Comment(0)
L
2

You Should Try

After Setting The Frame

NSArray *arr10 =[NSArray arrayWithObjects:btn1,btn2,nil];
for(UIButton *btn10 in arr10)
{
CAGradientLayer *btnGradient2 = [CAGradientLayer layer];
btnGradient2.frame = btn10.bounds;

btnGradient2.colors = [NSArray arrayWithObjects:
                       (id)[[UIColor colorWithRed:151.0/255.0f green:206.0/255.5 blue:99.0/255.0 alpha:1] CGColor],
                       (id)[[UIColor colorWithRed:126.0/255.0f green:192.0/255.5 blue:65.0/255.0 alpha:1]CGColor],
                       nil];
[btn10.layer insertSublayer:btnGradient2 atIndex:0];

}
Lascivious answered 7/11, 2013 at 6:33 Comment(0)
S
2

Swift 3.0

    let image = UIImage(named:"NoConnection")!

 warningButton = UIButton(type: .system)        
    warningButton.setImage(image, for: .normal)
    warningButton.tintColor = UIColor.lightText
    warningButton.frame = CGRect(origin: CGPoint(x:-100,y:0), size: CGSize(width: 59, height: 56))

    self.addSubview(warningButton)
Shikoku answered 30/9, 2016 at 19:48 Comment(0)
A
2

To set white colour of the image(arrow icon) on the button, we're using:

let imageOnButton = UIImage(named: "navForwardArrow")?.imageWithColor(color: UIColor.white)
button.setImage(imageOnButton, for: .normal)

Known issue: The icon looses its white colour while the button is pressed.

Screenshot: enter image description here

Amman answered 22/2, 2021 at 13:12 Comment(0)
G
1

Change button image or image view tint color Swift :

btn.imageView?.image = btn.imageView?.image?.withRenderingMode(.alwaysTemplate)

btn.imageView?.tintColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
Guardi answered 13/7, 2017 at 13:55 Comment(0)
S
1

Change button image or image view tint color Swift :

let image = UIImage(named: "map")
mapButton.setImage(image!.withRenderingMode(UIImage.RenderingMode.alwaysTemplate), for: .normal)
mapButton.tintColor = UIColor.black
Sewellel answered 21/2, 2023 at 11:24 Comment(0)
H
0

None of above worked for me, because tint was cleared after click. I had to use

button.setImageTintColor(Palette.darkGray(), for: UIControlState())
Herwin answered 26/9, 2018 at 20:23 Comment(0)
C
0

I had a problem with masking image in highlighted state. I didn't want it to happen. If You have the same problem, check this out: adjustsImageWhenHighlighted = false

Cell answered 8/4, 2022 at 11:45 Comment(0)
S
0

iOS 15+

If you have a UIButton based on an SF symbol and you created the button with a configuration, you can update the color after the fact as follows:

if var config = myButton.configuration {
      let image = config.image?.applyingSymbolConfiguration(UIImage.SymbolConfiguration(paletteColors: [.systemBlue]))
      config.image = image
      myButton.configuration = config
      myButton.setNeedsUpdateConfiguration()
}

Alternatively if you're trying to update the color of a button image you pulls from your app's assets catalog, this may do it:

if var config = myButton.configuration {
      let image = config.image?.withTintColor(.systemBlue, renderingMode: .alwaysTemplate)
      config.image = image
      myButton.configuration = config
      myButton.setNeedsUpdateConfiguration()
}
Sharma answered 6/5, 2023 at 23:12 Comment(1)
the first passage is an amazing tip!Holdover
D
0

Swift 5, using Storyboard:

Make sure you don't set the foreground color of the UIButton in Attributes Inspector(see image). Otherwise, if you set it, it will ignore all programmatic configurations!

Just let it be default.

1

Dochandorrach answered 5/7, 2023 at 23:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.