Trombone0904
Trombone0904

Reputation: 4268

set image color of a template image

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)

Upvotes: 11

Views: 10201

Answers (10)

Cyberbeni
Cyberbeni

Reputation: 710

Swift 5

This method should be used: (note the other answers, using NSImage.lockFocus() is deprecated macOS 10.0–13.0)

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

Note: When using .withAlphaComponent(0.5) on an NSColor instance, that color loses support for switching between light/dark mode. It's recommend to use color assets, to avoid the issue.

Upvotes: 14

Maetschl
Maetschl

Reputation: 1339

Swift 5 and SF Symbols 4.0

You can access to the full system icons api using SymbolConfiguration as this:

if var image = NSImage(systemSymbolName: "rainbow", accessibilityDescription: nil) {
    var config = NSImage.SymbolConfiguration(textStyle: .body, scale: .large)
    config = config.applying(.preferringMulticolor())
        //.applying(.init(hierarchicalColor: .green))
        //.applying(.init(paletteColors: [.blue, .blue, .green, .white, .systemTeal, .systemPink]))
    quitMenuItem.image = image.withSymbolConfiguration(config)
}

Upvotes: 1

hkdalex
hkdalex

Reputation: 755

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

Upvotes: 8

ingconti
ingconti

Reputation: 11666

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
}

}

Upvotes: 1

Tiep Vu Van
Tiep Vu Van

Reputation: 1005

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

Upvotes: 10

ocodo
ocodo

Reputation: 30319

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

Upvotes: 16

Trombone0904
Trombone0904

Reputation: 4268

I found the solution with everyone's help:

(Swift 3)

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)

Important: in interface builder I had to set the "render as" setting of the image to "Default".

Upvotes: 8

Seokhwan Cho
Seokhwan Cho

Reputation: 81

Had to modify @Ghost108's answer little bit for Xcode 9.2.

NSRectFillUsingOperation(imageRect, .sourceAtop)

to

imageRect.fill(using: .sourceAtop)

Thanks.

Upvotes: 6

Ghosty141
Ghosty141

Reputation: 21

Since you can't use the UIImage functions, you can try using CoreImage (CI). I don't know if there is an easier version but this one will work fore sure!

First you create the CIImage

    let image = CIImage(data: inputImage.tiffRepresentation!)

Now you can apply all kinds of filters and other stuff to the image, it's a really powerful tool.

The documentation for CI: https://developer.apple.com/documentation/coreimage

The Filter List: https://developer.apple.com/library/content/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html

Here is a simple filter example, you basically initialise a filter and then set the values for it, output it and repeat.

    let yourFilterName = CIFilter(name: "FilterName")
    yourFilterName!.setValue(SomeInputImage, forKey: kCIInputImageKey)
    yourFilterName!.setValue(10, forKey: kCIInputRadiusKey)
    let yourFilterName = yourFilterName!.outputImage

Now you can just convert the output back as NSImage.

    let cgimg = context.createCGImage(yourFilterName!, from: yourFilterName!.extent)
    let processedImage = NSImage(cgImage: cgimg!, size: NSSize(width: 0, height: 0))

Upvotes: 0

Jaykumar Patel
Jaykumar Patel

Reputation: 684

Try this code it helps.

Swift 3

let theImageView = UIImageView(image: UIImage(named:"foo")!.withRenderingMode(.alwaysTemplate))
theImageView.tintColor = UIColor.red

Upvotes: -6

Related Questions