- 
      
- 
        Save iamjason/a0a92845094f5b210cf8 to your computer and use it in GitHub Desktop. 
| /** | |
| Usage: | |
| let originalImage = UIImage(named: "cat") | |
| let tintedImage = originalImage.tintWithColor(UIColor(red: 0.9, green: 0.7, blue: 0.4, alpha: 1.0)) | |
| */ | |
| extension UIImage { | |
| func tintWithColor(color:UIColor)->UIImage { | |
| UIGraphicsBeginImageContext(self.size) | |
| let context = UIGraphicsGetCurrentContext() | |
| // flip the image | |
| CGContextScaleCTM(context, 1.0, -1.0) | |
| CGContextTranslateCTM(context, 0.0, -self.size.height) | |
| // multiply blend mode | |
| CGContextSetBlendMode(context, kCGBlendModeMultiply) | |
| let rect = CGRectMake(0, 0, self.size.width, self.size.height) | |
| CGContextClipToMask(context, rect, self.CGImage) | |
| color.setFill() | |
| CGContextFillRect(context, rect) | |
| // create uiimage | |
| let newImage = UIGraphicsGetImageFromCurrentImageContext() | |
| UIGraphicsEndImageContext() | |
| return newImage | |
| } | |
| } | |
I have this updated version for Swift 3 working well on my end:
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
}Here is everything with Retina, updates for Swift 3.1 and inclusion of proper import:
// Usage:
// let originalImage = UIImage(named: "cat")
// let tintedImage = originalImage.tintWithColor(UIColor(red: 0.9, green: 0.7, blue: 0.4, alpha: 1.0))
// reference: https://gist.github.com/iamjason/a0a92845094f5b210cf8
// modified to include retina
// Updated and tested for Swift 3.1
import UIKit
extension UIImage {
    
    func tint(with color: UIColor) -> UIImage
    {
        UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
        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
    }
}I would add the following just before the guard:
defer { UIGraphicsEndImageContext() }Obviously, remove it from the end of the method.
Maybe there's something I do not get, but using this gist doesn't seem to produce the expected result for me... Trying to tint the following texture:
Using this gist with texture.tint(with: UIColor(red:0, green:0, blue:1, alpha:1)) gives the following fully blue texture:
This other gist gives the following, correct according to me:
My bad, this does indeed seem to work (but in a different way, I guess it depends on your use case) when using grayscale images as the source.
However, here is the gist rewritten using the other one's structure. It seems to be quite a lot more memory efficient:
// Usage:
// let originalImage = UIImage(named: "cat")
// let tintedImage = originalImage.tint(UIColor(red: 0.9, green: 0.7, blue: 0.4, alpha: 1.0))
// reference: https://gist.github.com/iamjason/a0a92845094f5b210cf8
// modified to include retina
// Updated and tested for Swift 3.1
// refactored for memory purpose according to https://gist.github.com/lynfogeek/4b6ce0117fb0acdabe229f6d8759a139
import UIKit
extension UIImage {
    func tint(_ tintColor: UIColor?) -> UIImage {
        guard let tintColor = tintColor else { return self }
        return modifiedImage { context, rect in
            context.setBlendMode(.multiply)
            context.clip(to: rect, mask: self.cgImage!)
            tintColor.setFill()
            context.fill(rect)
        }
    }
    private func modifiedImage( draw: (CGContext, CGRect) -> ()) -> UIImage {
        // using scale correctly preserves retina images
        UIGraphicsBeginImageContextWithOptions(size, false, scale)
        defer { UIGraphicsEndImageContext() }
        guard let context = UIGraphicsGetCurrentContext() else { return self }
        // correctly rotate image
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        let rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        draw(context, rect)
        guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { return self }
        return newImage
    }
}after texture.tint(UIColor(red:0, green:0, blue:1, alpha:1)) it becomes:
Swift 4:
extension UIImage {
    
    func tinted(color: UIColor) -> UIImage {
        
        UIGraphicsBeginImageContext(self.size)
        guard let context = UIGraphicsGetCurrentContext() else { return self }
        guard let cgImage = cgImage else { return self }
        
        // flip the image
        context.scaleBy(x: 1.0, y: -1.0)
        context.translateBy(x: 0.0, y: -size.height)
        
        // multiply blend mode
        context.setBlendMode(.multiply)
        
        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        context.clip(to: rect, mask: cgImage)
        color.setFill()
        context.fill(rect)
        
        // create uiimage
        guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { return self }
        UIGraphicsEndImageContext()
        
        return newImage
        
    }
    
}Swift 3 with cap insets from original image (image will resize the way you set insets):
extension UIImage {
    func tint(with color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.main.scale)
        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.resizableImage(withCapInsets: self.capInsets, resizingMode: self.resizingMode)
    }
}Omit self when you don't need it:
func setTint(_ color: UIColor) -> UIImage {
    defer { UIGraphicsEndImageContext() }
    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
    guard let context = UIGraphicsGetCurrentContext() else { return self }
    // flip the image
    context.scaleBy(x: 1.0, y: -1.0)
    context.translateBy(x: 0.0, y: -size.height)
    // multiply blend mode
    context.setBlendMode(.multiply)
    let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
    context.clip(to: rect, mask: cgImage!)
    color.setFill()
    context.fill(rect)
    // create UIImage
    guard let newImage = UIGraphicsGetImageFromCurrentImageContext() else { return self }
    return newImage
}




If you want add retina images support, there is a simpler solution:
UIGraphicsBeginImageContextWithOptions(self.size, false, UIScreen.mainScreen().scale)