64

I have a UIImage that is a small symbol that is all black. The UIImage is getting set in a custom UIButton subclass I have. Is it possible to have the image to apply the tintColor to it, so instead of the black image it changes colors to whatever the tintColor is?

I'm just trying to avoid creating new assets.

// here I want defaultImageName (that is black) to use the tintColor (that is white)
[self setImage:[UIImage imageNamed:defaultImageName] forState:UIControlStateNormal];
AlBlue
  • 23,254
  • 14
  • 71
  • 91
aahrens
  • 5,522
  • 7
  • 39
  • 63
  • 12
    This isn't a duplicate of the linked question. – lassej Oct 08 '14 at 16:12
  • please see this [link](http://stackoverflow.com/questions/19274789/how-can-i-change-image-tintcolor-in-ios-and-watchkit) change image tintColor in iOS and WatchKit – M.Ganji Aug 25 '16 at 09:34

8 Answers8

79

If you are just supporting iOS 7 you can use tintColor and UIImageRenderingModeAlwaysTemplate

This article covers that:

https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming

If you need to support an earlier version you may want to consider this thread

How would I tint an image programmatically on the iPhone?

Community
  • 1
  • 1
madmik3
  • 6,975
  • 3
  • 38
  • 60
  • 2
    Please note that using templates might impact performance since it is always composited on the fly –  Dec 02 '14 at 12:10
  • 3
    You need to setup your UIImage so it draws the image as a template image, ignoring its color information: `UIImage *image = [[UIImage imageNamed:@"image.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];`. And then the tint color will be applied, set that on your UIImageView: `_imageView.tintColor = [UIColor whiteColor];` – Scott Fister Jul 18 '18 at 16:58
  • 1
    Unfortunately the first Llink is dead – Kamajabu May 29 '20 at 13:56
  • [archive](https://web.archive.org/web/20150510110717/https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming) link –  Dec 14 '20 at 14:32
  • 2
    LINK is dead. Please update this answer with the correct details – TimBigDev Apr 22 '21 at 01:02
26

Swift 4, copy-paste solution

@IBOutlet weak var iconImageView: UIImageView!
iconImageView.image = UIImage(imageLiteralResourceName: "myImageName").withRenderingMode(.alwaysTemplate)
iconImageView.tintColor = UIColor.red
zgorawski
  • 2,597
  • 4
  • 30
  • 43
  • 1
    WARNING: I would like to add that if you perform any task that returns a new image, you WILL need to set `withRenderingMode(.alwaysTemplate)` again. Example: if you use `UIGraphicsImageRenderer` to resize image. This also applies if you set an asset image to Template rendering mode. – OzzyTheGiant Oct 31 '19 at 16:55
10

On iOS 13+ you can use the following:

UIImage(named: "img_name")?.withTintColor(.red)

https://developer.apple.com/documentation/uikit/uiimage/3327300-withtintcolor

Zorayr
  • 23,770
  • 8
  • 136
  • 129
barola_mes
  • 1,532
  • 2
  • 13
  • 16
9

Try this:

func tinted(with color: UIColor) -> UIImage? {
    defer { UIGraphicsEndImageContext() }
    UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
    color.set()
    self.withRenderingMode(.alwaysTemplate).draw(in: CGRect(origin: .zero, size: self.size))
    return UIGraphicsGetImageFromCurrentImageContext()
}

For example:

button.setImage(UIImage(systemName: "checkmark.circle")?.tinted(with: .systemGray), for: .normal)
jqgsninimo
  • 6,562
  • 1
  • 36
  • 30
2

Here's how I use tint colors and opacities in IOS 9 with Swift -

//apply a color to an image
//ref - http://stackoverflow.com/questions/28427935/how-can-i-change-image-tintcolor
//ref - https://www.captechconsulting.com/blogs/ios-7-tutorial-series-tint-color-and-easy-app-theming
func getTintedImage() -> UIImageView {

    var image :UIImage
    var imageView :UIImageView

    image = UIImage(named: "someAsset")!
    let size  : CGSize = image.size
    let frame : CGRect = CGRectMake((UIScreen.mainScreen().bounds.width-86)/2, 600, size.width, size.height)

    let redCover : UIView = UIView(frame: frame)

    redCover.backgroundColor = UIColor.redColor()
    redCover.layer.opacity = 0.75

    imageView = UIImageView();
    imageView.image = image.imageWithRenderingMode(UIImageRenderingMode.Automatic)

    imageView.addSubview(redCover)

    return imageView
}
Ondrej Rafaj
  • 4,342
  • 8
  • 42
  • 65
J-Dizzle
  • 4,861
  • 4
  • 40
  • 50
  • 4
    I dislike this solution because it doesn't tint the image. Instead it reduces the contrast by using a second layer and making one of the (depends on z order) semi transparent. You also can never achieve a satisfying saturation of the tint color. – cybergen Jun 29 '16 at 13:28
  • 1
    quite interesting. Please send over or post here some tips or url links to help me then generate and post here an alternative solution! :) – J-Dizzle Jun 29 '16 at 17:12
1

Why not use image filtering

btn.setImage(image, for: UIControl.State.normal)
btn.setImage(image.disabled, for: UIControl.State.disabled)

Use CoreImage to do image filter

extension UIImage
{
    /// Create a grayscale image with alpha channel. Is 5 times faster than grayscaleImage().
    /// - Returns: The grayscale image of self if available.
    var disabled: UIImage?
    {
        // Create image rectangle with current image width/height * scale
        let pixelSize = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
        let imageRect = CGRect(origin: CGPoint.zero, size: pixelSize)
        // Grayscale color space
         let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()

            // Create bitmap content with current image size and grayscale colorspace
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
        if let context: CGContext = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
            {
                // Draw image into current context, with specified rectangle
                // using previously defined context (with grayscale colorspace)
                guard let cg = self.cgImage else{
                    return nil
                }
                context.draw(cg, in: imageRect)
                // Create bitmap image info from pixel data in current context
                if let imageRef: CGImage = context.makeImage(){
                    let bitmapInfoAlphaOnly = CGBitmapInfo(rawValue: CGImageAlphaInfo.alphaOnly.rawValue)

                    guard let context = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfoAlphaOnly.rawValue) else{
                        return nil
                    }
                    context.draw(cg, in: imageRect)
                    if let mask: CGImage = context.makeImage() {
                        // Create a new UIImage object
                        if let newCGImage = imageRef.masking(mask){
                            // Return the new grayscale image
                            return UIImage(cgImage: newCGImage, scale: self.scale, orientation: self.imageOrientation)
                        }
                    }

                }
            }


        // A required variable was unexpected nil
        return nil
    }
}

Of course, in Swift 5

black_pearl
  • 2,549
  • 1
  • 23
  • 36
1

Swift5 Extension

extension UIImage {
    var template: UIImage? {
         return self.withRenderingMode(.alwaysTemplate)
    }
}

Usage:

  • UIImageView

    let imgView = UIImageView()
    imgView.tintColor = UIColor.red
    imgView.image = UIImage(named: "IMAGE_NAME_HERE")?.template
    
  • UIButton

    let button = UIButton(type: .custom)
    button.tintColor = UIColor.red
    button.setImage(UIImage(named: "IMAGE_NAME_HERE")?.template, for: .normal)
    
Li Jin
  • 1,879
  • 2
  • 16
  • 23
0

Use this simple extension to UIImageView

@IBInspectable var tintedColor: UIColor{
    get{
        return tintColor
    }
    set{
        image = image?.withRenderingMode(.alwaysTemplate)
        tintColor = newValue
    }
}
Milind Chaudhary
  • 1,632
  • 1
  • 17
  • 16