Reputation: 4268
(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
Reputation: 710
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
Reputation: 1339
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
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
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
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
Reputation: 30319
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
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
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
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
Reputation: 684
Try this code it helps.
Swift 3
let theImageView = UIImageView(image: UIImage(named:"foo")!.withRenderingMode(.alwaysTemplate))
theImageView.tintColor = UIColor.red
Upvotes: -6