26
votes

I am using a "spinner" NSProgressIndicator in my cocoa app:

spinner image

I would like to display it in a different color so that it will show up well on a dark background:

inverted spinner image

How would I go about doing this? My last resort would be to write my own custom NSView subclass that renders a custom animation, but I'm not even sure where to start on that front. Any help is appreciated.

9

9 Answers

26
votes

This is what I've done:

    #import <QuartzCore/QuartzCore.h>

    ...

    CIFilter *lighten = [CIFilter filterWithName:@"CIColorControls"];
    [lighten setDefaults];
    [lighten setValue:@1 forKey:@"inputBrightness"];
    [self.indicator setContentFilters:[NSArray arrayWithObjects:lighten, nil]];
20
votes

I actually have implemented clones of the spinning NSProgressIndicator that might suit your needs. They can be drawn at any size and in any color. One is a subclass of NSView, which can be used on OS X 10.4, and the other is a subclass of CALayer, which can be used in a CoreAnimation-based project. The code is on github (both the NSView-based version and the CoreAnimation-based version), and there is a post with some screenshots on my blog.

15
votes

Not sure if this would work correctly with NSProgressIndicator, but you might try using a Core Image filter to invert the display of the progress indicator view. You would have to make the view layer backed, and then add a CIFilter to its layer's filters. You may be able to do this all in the effects inspector in Interface Builder, otherwise you could also just do it in code.

5
votes

While I'm sure that Kelan's code worked a while ago, it's difficult to update. I ended up going with ITProgressIndicator, and it took about 2 minutes to get working using Xcode 6 (Beta 1) and Yosemite (Beta 2).

5
votes

For a more fine grained solution, you can use a polynomial color approach using the following Category. Please note that for simplicity I use only the x component of the vectors. For more accurate color matching please see reference at: https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CIColorMatrix

@import QuartzCore;
#import <Cocoa/Cocoa.h>

@interface NSProgressIndicator (Colors)

- (void)setCustomColor:(NSColor *)aColor;

@end

@implementation NSProgressIndicator (Colors)

- (void)setCustomColor:(NSColor *)aColor {
    CIFilter *colorPoly = [CIFilter filterWithName:@"CIColorPolynomial"];
    [colorPoly setDefaults];

    CIVector *redVector = [CIVector vectorWithX:aColor.redComponent Y:0 Z:0 W:0];
    CIVector *greenVector = [CIVector vectorWithX:aColor.greenComponent Y:0 Z:0 W:0];
    CIVector *blueVector = [CIVector vectorWithX:aColor.blueComponent Y:0 Z:0 W:0];
    [colorPoly setValue:redVector forKey:@"inputRedCoefficients"];
    [colorPoly setValue:greenVector forKey:@"inputGreenCoefficients"];
    [colorPoly setValue:blueVector forKey:@"inputBlueCoefficients"];
    [self setContentFilters:[NSArray arrayWithObjects:colorPoly, nil]];
}

@end
3
votes

If you’re targeting Mojave or later, you can just set the spinner’s appearance to dark aqua. This will always give you a white spinner, regardless of the user’s system appearance.

spinner.appearance = NSAppearance(named: .darkAqua)
1
votes

In Swift:

override func viewDidLoad() {
    super.viewDidLoad()
    let brightness = CIFilter(name: "CIColorControls")!
    brightness.setDefaults()
    brightness.setValue(1, forKey: "inputBrightness")
    self.spinner.contentFilters = [brightness]
}
0
votes

I've seen some third party HUD-style control frameworks that include the bar style NSProgressIndicator, but unfortunately I don't remember ever seeing the spinner. If you can't find a way to get it to do what you want, this page can generate an animated gif that might be helpful for making your own.

0
votes

Swift 4 Solution

guard let lighten = CIFilter(name: "CIColorControls") else { return }
lighten.setDefaults()
lighten.setValue(1, forKey: "inputBrightness")
contentFilters = [lighten]