How can I color a UIImage in Swift?
Asked Answered
S

22

123

I have an image called arrowWhite. I want to colour this image to black.

func attachDropDownArrow() -> NSMutableAttributedString {
    let image:UIImage = UIImage(named: "arrowWhite.png")!
    let attachment = NSTextAttachment()
    attachment.image = image
    attachment.bounds = CGRectMake(2.25, 2, attachment.image!.size.width - 2.25, attachment.image!.size.height - 2.25)
    let attachmentString = NSAttributedString(attachment: attachment)
    let myString = NSMutableAttributedString(string: NSString(format: "%@", self.privacyOptions[selectedPickerRow]) as String)
    myString.appendAttributedString(attachmentString)
    return myString
}

I want to get this image in blackColour.
tintColor is not working...

Spiegleman answered 4/8, 2015 at 7:28 Comment(3)
doable from the Interface Builder, see @Harry Bloom pretty far down belowMultidisciplinary
Most elegant solution: https://mcmap.net/q/182268/-how-can-i-change-the-color-of-a-uiimage-duplicateFarr
Nowadays it is this easy: yourIcon.image = yourIcon.image?.withRenderingMode(.alwaysTemplate) then set the tintDeclare
L
241

Swift 4 and 5

extension UIImageView {
  func setImageColor(color: UIColor) {
    let templateImage = self.image?.withRenderingMode(.alwaysTemplate)
    self.image = templateImage
    self.tintColor = color
  }
}

Call like this:

let imageView = UIImageView(image: UIImage(named: "your_image_name"))
imageView.setImageColor(color: UIColor.purple)

Alternativ For Swift 3, 4 or 5

extension UIImage {

    func maskWithColor(color: UIColor) -> UIImage? {
        let maskImage = cgImage!

        let width = size.width
        let height = size.height
        let bounds = CGRect(x: 0, y: 0, width: width, height: height)

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!

        context.clip(to: bounds, mask: maskImage)
        context.setFillColor(color.cgColor)
        context.fill(bounds)

        if let cgImage = context.makeImage() {
            let coloredImage = UIImage(cgImage: cgImage)
            return coloredImage
        } else {
            return nil
        }
    }

}

For Swift 2.3

extension UIImage {
func maskWithColor(color: UIColor) -> UIImage? {

    let maskImage = self.CGImage
    let width = self.size.width
    let height = self.size.height
    let bounds = CGRectMake(0, 0, width, height)

    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)
    let bitmapContext = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, colorSpace, bitmapInfo.rawValue) //needs rawValue of bitmapInfo

    CGContextClipToMask(bitmapContext, bounds, maskImage)
    CGContextSetFillColorWithColor(bitmapContext, color.CGColor)
    CGContextFillRect(bitmapContext, bounds)

    //is it nil?
    if let cImage = CGBitmapContextCreateImage(bitmapContext) {
        let coloredImage = UIImage(CGImage: cImage)

        return coloredImage

    } else {
        return nil
    } 
 }
}

Call like this:

let image = UIImage(named: "your_image_name")
testImage.image =  image?.maskWithColor(color: UIColor.blue)
Lacto answered 13/4, 2016 at 7:8 Comment(8)
Nice start, but the result is grainy. As @Darko mentions below, I believe it is because you are not taking scale and other parameters into account.Platinous
same for me - pixelated imageVincents
Worked prefect for me in Swift 3. Thanks!!Systematist
Doesn't preserve scaling and orientation.Dorothadorothea
inside extension, function should be publicSizemore
I do not know exactly. The added benefit would be that third parties (pods, libraries ...) could use this extension. If you like you can set this function to public. I do not see any disadvantages for that.Lacto
This solution works but image gets blurry so its uselessClearcut
Yeah, poorly thats true. If you use an image with high resolution and use the different layers @2 and @3 it should be okayLacto
H
98

There's a built in method to obtain a UIImage that is automatically rendered in template mode. This uses a view's tintColor to color the image:

let templateImage = originalImage.imageWithRenderingMode(UIImageRenderingModeAlwaysTemplate)
myImageView.image = templateImage
myImageView.tintColor = UIColor.orangeColor()
Hammons answered 13/4, 2016 at 7:27 Comment(8)
This is the best answer - more info may be found in Apple Docs - developer.apple.com/library/content/documentation/…Frigid
See Swift 3 syntax for rendering mode here: https://mcmap.net/q/182269/-creating-uiimage-with-renderingmode-in-swiftSiphon
using imageview is obvious, but we want UIImage onlyPatriciate
not a solution if you are working with UIImage objects independently of UIImageView's. This only works if you have access to UIImageViewDelgado
Works great even in case when myImageView is a UIButtonGuerrero
This solution perfectly worked for me & even image retains its quality. Great work.Clearcut
you provided method to imageview. i know this. how to do the same with out imageview?Dehaven
title is how to color IMAGE, not imageView... :)Dispatch
N
50

First you have to change the rendering property of the image to "Template Image" in the .xcassets folder. You can then just change the tint color property of the instance of your UIImageView like so:

imageView.tintColor = UIColor.whiteColor()

enter image description here

Netherlands answered 12/11, 2016 at 17:49 Comment(3)
Did tintColor get removed from UIImage at some point? I was excited about this answer, but it doesn't seem to exist in iOS 10Trench
Hey @TravisGriggs. Sorry, I have just edited my answer to be a bit more descriptive, the tintColor property is on UIImageView, not UIImageNetherlands
Tx, this is so cool! Note: The Tint appears in the View section of the ImageView inspector, a little further down. Just to be extra clear.Multidisciplinary
P
40

I ended up with this because other answers either lose resolution or work with UIImageView, not UIImage, or contain unnecessary actions:

Swift 3

extension UIImage {
    
    public func mask(with color: UIColor) -> UIImage {
        
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        let context = UIGraphicsGetCurrentContext()!
        
        let rect = CGRect(origin: CGPoint.zero, size: size)
        
        color.setFill()
        self.draw(in: rect)
        
        context.setBlendMode(.sourceIn)
        context.fill(rect)
        
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return resultImage
    }
    
}
Polyglot answered 2/6, 2017 at 9:45 Comment(4)
best answer in here, keeps the same image orientation and qualityAbstractionism
Yeah, I've tested all the above answers and this indeed takes the scale into consideration, so it won't give you pixelated UIImages. Really nice answer, thank you!Hit
If you have an image with a transparent background, you can set blend mode to .destinationAtop. This lets you color the foreground of the image, leaving the background untouched.Prance
This is the best answer if you are working with just UIImages and not UIViews.Goaltender
O
23

This function uses core graphics to achieve this.

func overlayImage(color: UIColor) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
    let context = UIGraphicsGetCurrentContext()

    color.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 coloredImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return coloredImage
}
Olathe answered 5/6, 2016 at 17:8 Comment(3)
This works, where the other top two answers are wrong.Rhetor
Yes this works perfectly. maskWithColor extension works but that ignores scale so image does not look sharp on higher resolution devices.Expediential
This works perfect!! We use in a extension and runs OK. Others solutions ignore scale...Laughing
P
20

For swift 4.2 to change UIImage color as you want (solid color)

extension UIImage {
    func imageWithColor(color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color.setFill()

        let context = UIGraphicsGetCurrentContext()
        context?.translateBy(x: 0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.setBlendMode(CGBlendMode.normal)

        let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
        context?.clip(to: rect, mask: self.cgImage!)
        context?.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }
}

How to use

self.imgVw.image = UIImage(named: "testImage")?.imageWithColor(UIColor.red)
Paxon answered 14/2, 2019 at 12:37 Comment(0)
D
11

I found the solution by H R to be most helpful but adapted it slightly for Swift 3

extension UIImage {

    func maskWithColor( color:UIColor) -> UIImage {

         UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
         let context = UIGraphicsGetCurrentContext()!

         color.setFill()

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

         let rect = CGRect(x: 0.0, y: 0.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 coloredImage = UIGraphicsGetImageFromCurrentImageContext()
         UIGraphicsEndImageContext()

         return coloredImage!
    }
}

This takes into consideration scale and also does not produce a lower res image like some other solutions. Usage :

image = image.maskWithColor(color: .green )
Delgado answered 22/2, 2017 at 19:30 Comment(2)
This is definitely the implementation to use (disambiguating from other ones on this page).Latitudinarian
To be extra safe the force unwraps should be wrapped in if-lets or a guard.Dash
T
10

Create an extension on UIImage:

/// UIImage Extensions
extension UIImage {
    func maskWithColor(color: UIColor) -> UIImage {

        var maskImage = self.CGImage
        let width = self.size.width
        let height = self.size.height
        let bounds = CGRectMake(0, 0, width, height)

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.PremultipliedLast.rawValue)
        let bitmapContext = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, colorSpace, bitmapInfo)

        CGContextClipToMask(bitmapContext, bounds, maskImage)
        CGContextSetFillColorWithColor(bitmapContext, color.CGColor)
        CGContextFillRect(bitmapContext, bounds)

        let cImage = CGBitmapContextCreateImage(bitmapContext)
        let coloredImage = UIImage(CGImage: cImage)

        return coloredImage!
    }
}

Then you can use it like that:

image.maskWithColor(UIColor.redColor())
Tops answered 4/8, 2015 at 7:44 Comment(1)
This ignores scale, orientation and other parameters of UIImage.Hammons
B
9

For iOS13+ there are withTintColor(__:) and withTintColor(_:renderingMode:) methods.

Example usage:

let newImage = oldImage.withTintColor(.red)

or

let newImage = oldImage.withTintColor(.red, renderingMode: .alwaysTemplate)
Brasilein answered 7/5, 2020 at 3:17 Comment(0)
S
6

Swift 3 extension wrapper from @Nikolai Ruhe answer.

extension UIImageView {

    func maskWith(color: UIColor) {
        guard let tempImage = image?.withRenderingMode(.alwaysTemplate) else { return }
        image = tempImage
        tintColor = color
    }

}

It can be use for UIButton as well, e.g:

button.imageView?.maskWith(color: .blue)
Saurischian answered 12/5, 2017 at 11:28 Comment(0)
K
6

Add this extension in your code and change image color in storyboard itself.

Swift 4 & 5:

extension UIImageView {
    @IBInspectable
    var changeColor: UIColor? {
        get {
            let color = UIColor(cgColor: layer.borderColor!);
            return color
        }
        set {
            let templateImage = self.image?.withRenderingMode(.alwaysTemplate)
            self.image = templateImage
            self.tintColor = newValue
        }
    }
}

Storyboard Preview:

enter image description here

Kirkendall answered 5/7, 2019 at 7:46 Comment(0)
S
5

Swift 4

 let image: UIImage? =  #imageLiteral(resourceName: "logo-1").withRenderingMode(.alwaysTemplate)
    topLogo.image = image
    topLogo.tintColor = UIColor.white
Statued answered 16/12, 2017 at 7:3 Comment(0)
D
5

Simpleminded way:

yourIcon.image = yourIcon.image?.withRenderingMode(.alwaysTemplate)
yourIcon.tintColor = .someColor

BTW it's more fun on Android!

yourIcon.setColorFilter(getColor(R.color.someColor), PorterDuff.Mode.MULTIPLY);
Declare answered 24/4, 2021 at 17:11 Comment(2)
This should be the accepted answer! Very straightforward solution!Unmarked
@Unmarked - right, nowadays it is very easy. A huge problem with Stackoverflow is that, you get answers which change drastically over the many years. This question is some ten years old - the answers from that era are of no value now. It's a difficult problem on SO.Declare
D
4

Add extension Function:

extension UIImageView {
    func setImage(named: String, color: UIColor) {
        self.image = #imageLiteral(resourceName: named).withRenderingMode(.alwaysTemplate)
        self.tintColor = color
    }
}

Use like:

anyImageView.setImage(named: "image_name", color: .red)
Demosthenes answered 26/12, 2019 at 10:39 Comment(0)
J
2

Post iOS 13 you can use it something like this

arrowWhiteImage.withTintColor(.black, renderingMode: .alwaysTemplate)
Jenks answered 11/12, 2019 at 14:24 Comment(0)
H
1

Swift 3

21 June 2017

I use CALayer to mask the given image with Alpha Channel

import Foundation


extension UIImage {

    func maskWithColor(color: UIColor) -> UIImage? {
    
        let maskLayer = CALayer()
        maskLayer.bounds = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        maskLayer.backgroundColor = color.cgColor
        maskLayer.doMask(by: self)
        let maskImage = maskLayer.toImage()
        return maskImage
    }

}


extension CALayer {
    func doMask(by imageMask: UIImage) {
        let maskLayer = CAShapeLayer()
        maskLayer.bounds = CGRect(x: 0, y: 0, width: imageMask.size.width, height: imageMask.size.height)
        bounds = maskLayer.bounds
        maskLayer.contents = imageMask.cgImage
        maskLayer.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
        mask = maskLayer
    }

    func toImage() -> UIImage?
    {
        UIGraphicsBeginImageContextWithOptions(bounds.size,
                                               isOpaque,
                                               UIScreen.main.scale)
        guard let context = UIGraphicsGetCurrentContext() else {
            UIGraphicsEndImageContext()
            return nil
        }
        render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }
}
Hedley answered 21/6, 2017 at 10:17 Comment(0)
D
1

Swift 3 version with scale and Orientation from @kuzdu answer

extension UIImage {

    func mask(_ color: UIColor) -> UIImage? {
        let maskImage = cgImage!

        let width = (cgImage?.width)!
        let height = (cgImage?.height)!
        let bounds = CGRect(x: 0, y: 0, width: width, height: height)

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)!

        context.clip(to: bounds, mask: maskImage)
        context.setFillColor(color.cgColor)
        context.fill(bounds)

        if let cgImage = context.makeImage() {
            let coloredImage = UIImage.init(cgImage: cgImage, scale: scale, orientation: imageOrientation)
            return coloredImage
        } else {
            return nil
        }
    }
}
Dorothadorothea answered 15/9, 2017 at 11:34 Comment(0)
M
1

Swift 4.

Use this extension to create a solid colored image

extension UIImage {   

    public func coloredImage(color: UIColor) -> UIImage? {
        return coloredImage(color: color, size: CGSize(width: 1, height: 1))
    }

    public func coloredImage(color: UIColor, size: CGSize) -> UIImage? {

        UIGraphicsBeginImageContextWithOptions(size, false, 0)

        color.setFill()
        UIRectFill(CGRect(origin: CGPoint(), size: size))

        guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return nil }
        UIGraphicsEndImageContext()

        return image
    }
}
Mcclintock answered 16/1, 2018 at 8:31 Comment(1)
Worked prefect for me in navigationBar.setBackgroundImage and Swift 5. Thanks!Fallen
E
0

Here is swift 3 version of H R's solution.

func overlayImage(color: UIColor) -> UIImage? {
    UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
    let context = UIGraphicsGetCurrentContext()

    color.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 coloredImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return coloredImage
}
Expediential answered 15/5, 2017 at 7:7 Comment(0)
A
0

If you want to change the tint color of UIImage. Use simple two-line code:

ImageView.image = ImageView.image?.withRenderingMode(.alwaysTemplate)
ImageView.tintColor = UIColor.black

Where image view is your image outlet.

Awfully answered 20/9, 2023 at 8:28 Comment(0)
C
-1

Since I found Darko's answer very helpful in colorizing custom pins for mapView annotations, but had to do some conversions for Swift 3, thought I'd share the updated code along with my recommendation for his answer:

extension UIImage {
    func maskWithColor(color: UIColor) -> UIImage {

        var maskImage = self.CGImage
        let width = self.size.width
        let height = self.size.height
        let bounds = CGRect(x: 0, y: 0, width: width, height: height)

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
        let bitmapContext = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)

        bitmapContext!.clip(to: bounds, mask: maskImage!)
        bitmapContext!.setFillColor(color.cgColor)
        bitmapContext!.fill(bounds)

        let cImage = bitmapContext!.makeImage()
        let coloredImage = UIImage(CGImage: cImage)

        return coloredImage!
    }
}
Cannonade answered 17/9, 2016 at 19:31 Comment(0)
C
-1

I have modified the extension found here: Github Gist, for Swift 3 which I have tested in the context of an extension for UIImage.

func tint(with color: UIColor) -> UIImage 
{
   UIGraphicsBeginImageContext(self.size)
   guard let context = UIGraphicsGetCurrentContext() else { return self }

   // flip the image
   context.scaleBy(x: 1.0, y: -1.0)
   context.translateBy(x: 0.0, y: -self.size.height)

   // multiply blend mode
   context.setBlendMode(.multiply)

   let rect = 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)

   // create UIImage
   guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { return self }
   UIGraphicsEndImageContext()

   return newImage
}
Cantone answered 5/7, 2017 at 16:7 Comment(1)
works fine, work in swift 5.x AND answer to question ! :)Dispatch

© 2022 - 2025 — McMap. All rights reserved.