How can I color a UIImage in Swift?

128,952

Solution 1

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)

Solution 2

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

Solution 3

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

Solution 4

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
    }
    
}

Solution 5

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
}
Share:
128,952

Related videos on Youtube

Sujisha Os
Author by

Sujisha Os

Updated on April 25, 2021

Comments

  • Sujisha Os
    Sujisha Os about 3 years

    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...

    • Andy Weinstein
      Andy Weinstein almost 4 years
      doable from the Interface Builder, see @Harry Bloom pretty far down below
    • Umair Ali
      Umair Ali almost 4 years
      Most elegant solution: stackoverflow.com/a/63167556/2692839
    • Fattie
      Fattie about 3 years
      Nowadays it is this easy: yourIcon.image = yourIcon.image?.withRenderingMode(.alwaysTemplate) then set the tint
  • Nikolai Ruhe
    Nikolai Ruhe about 8 years
    This ignores scale, orientation and other parameters of UIImage.
  • TruMan1
    TruMan1 about 8 years
    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.
  • chrysb
    chrysb almost 8 years
    This works, where the other top two answers are wrong.
  • Mark
    Mark over 7 years
    This is the best answer - more info may be found in Apple Docs - developer.apple.com/library/content/documentation/…
  • Phil_Ken_Sebben
    Phil_Ken_Sebben over 7 years
    See Swift 3 syntax for rendering mode here: stackoverflow.com/a/24145287/448718
  • djdance
    djdance over 7 years
    using imageview is obvious, but we want UIImage only
  • Pez
    Pez about 7 years
    not a solution if you are working with UIImage objects independently of UIImageView's. This only works if you have access to UIImageView
  • Travis Griggs
    Travis Griggs about 7 years
    Did tintColor get removed from UIImage at some point? I was excited about this answer, but it doesn't seem to exist in iOS 10
  • Harry Bloom
    Harry Bloom about 7 years
    Hey @TravisGriggs. Sorry, I have just edited my answer to be a bit more descriptive, the tintColor property is on UIImageView, not UIImage
  • Scott Corscadden
    Scott Corscadden about 7 years
    This is definitely the implementation to use (disambiguating from other ones on this page).
  • Moin Uddin
    Moin Uddin almost 7 years
    Yes this works perfectly. maskWithColor extension works but that ignores scale so image does not look sharp on higher resolution devices.
  • Async-
    Async- almost 7 years
    same for me - pixelated image
  • oscar castellon
    oscar castellon over 6 years
    Worked prefect for me in Swift 3. Thanks!!
  • Shailesh
    Shailesh over 6 years
    Doesn't preserve scaling and orientation.
  • SmartTree
    SmartTree over 6 years
    best answer in here, keeps the same image orientation and quality
  • daxh
    daxh over 6 years
    Works great even in case when myImageView is a UIButton
  • Chris Paveglio
    Chris Paveglio over 6 years
    To be extra safe the force unwraps should be wrapped in if-lets or a guard.
  • Shivam Pokhriyal
    Shivam Pokhriyal over 5 years
    inside extension, function should be public
  • kuzdu
    kuzdu over 5 years
    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.
  • Javi Campaña
    Javi Campaña over 5 years
    This works perfect!! We use in a extension and runs OK. Others solutions ignore scale...
  • Guilherme Matuella
    Guilherme Matuella over 5 years
    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!
  • Mayur
    Mayur almost 5 years
    This solution works but image gets blurry so its useless
  • Mayur
    Mayur almost 5 years
    This solution perfectly worked for me & even image retains its quality. Great work.
  • kuzdu
    kuzdu almost 5 years
    Yeah, poorly thats true. If you use an image with high resolution and use the different layers @2 and @3 it should be okay
  • swift2geek
    swift2geek over 4 years
    you provided method to imageview. i know this. how to do the same with out imageview?
  • Jesse
    Jesse about 4 years
    Worked prefect for me in navigationBar.setBackgroundImage and Swift 5. Thanks!
  • Andy Weinstein
    Andy Weinstein almost 4 years
    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.
  • Shaked Sayag
    Shaked Sayag over 3 years
    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.
  • ingconti
    ingconti over 3 years
    title is how to color IMAGE, not imageView... :)
  • ingconti
    ingconti over 3 years
    works fine, work in swift 5.x AND answer to question ! :)
  • chlkdst
    chlkdst about 3 years
    This should be the accepted answer! Very straightforward solution!
  • Fattie
    Fattie about 3 years
    @chlkdst - 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.