set image color of a template image

Viewed 8050

I have an image like this: enter image description here

(Rendered as a template image)

I tried this code:

@IBOutlet weak var imgAdd: NSImageView!
imgAdd.layer?.backgroundColor = CGColor.white

Which only changes the background color of course.

Is there a way to change the color of this image programmatically?


So far I've tried the code below which doesn't work. (The image color doesn't change.)

func tintedImage(_ image: NSImage, tint: NSColor) -> NSImage {
    guard let tinted = image.copy() as? NSImage else { return image }
    tinted.lockFocus()
    tint.set()

    let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
    NSRectFillUsingOperation(imageRect, .sourceAtop)

    tinted.unlockFocus()
    return tinted
}

imgDok.image = tintedImage(NSImage(named: "myImage")!, tint: NSColor.red)
9 Answers

Swift 4

Updated answer for Swift 4

Please note, this NSImage extension is based on @Ghost108 and @Taehyung_Cho's answers, so a larger credit goes to them.

extension NSImage {
    func tint(color: NSColor) -> NSImage {
        let image = self.copy() as! NSImage
        image.lockFocus()

        color.set()

        let imageRect = NSRect(origin: NSZeroPoint, size: image.size)
        imageRect.fill(using: .sourceAtop)

        image.unlockFocus()

        return image
    }
}

Swift 4 version

extension NSImage {
   func image(withTintColor tintColor: NSColor) -> NSImage {
       guard isTemplate else { return self }
       guard let copiedImage = self.copy() as? NSImage else { return self }
       copiedImage.lockFocus()
       tintColor.set()
       let imageBounds = NSMakeRect(0, 0, copiedImage.size.width, copiedImage.size.height)
       imageBounds.fill(using: .sourceAtop)
       copiedImage.unlockFocus()
       copiedImage.isTemplate = false
       return copiedImage
   }
}

The other solutions don't work when the user wants to change between light and dark mode, this method solves that:

extension NSImage {
    func tint(color: NSColor) -> NSImage {
        return NSImage(size: size, flipped: false) { (rect) -> Bool in
            color.set()
            rect.fill()
            self.draw(in: rect, from: NSRect(origin: .zero, size: self.size), operation: .destinationIn, fraction: 1.0)
            return true
        }
    }
}

Be aware that if you use .withAlphaComponent(0.5) on an NSColor instance, that color loses support for switching between light/dark mode. I recommend using color assets to avoid that issue.

Since your image is inside an NSImageView, the following should work fine (available since macOS 10.14):

let image = NSImage(named: "myImage")!
image.isTemplate = true
let imageView = NSImageView(image: image)
imageView.contentTintColor = .green

The solution is to apply "contentTintColor" to your NSImageView instead of the NSImage.

See: Documentation

no need to copt: extension NSImage {

func tint(with color: NSColor) -> NSImage {
    self.lockFocus()
    color.set()
    let srcSpacePortionRect = NSRect(origin: CGPoint(), size: self.size)
    srcSpacePortionRect.fill(using: .sourceAtop)
    self.unlockFocus()
    return self
}

}

Related