8

When I set my progress indicator's background to green, it works as expected.

Code:

loadingIndicator.wantsLayer = true
loadingIndicator.layer?.backgroundColor = NSColor.greenColor().CGColor

Result:

enter image description here

But when I set the background color to white, the background color stays gray (instead of changing to white).

Code:

loadingIndicator.wantsLayer = true
loadingIndicator.layer?.backgroundColor = NSColor.whiteColor().CGColor

Result:

enter image description here

Why does setting the background color to white not work, but green does? How can I correct this issue? I'm on OS X 10.11.4.

EDIT: This apparently only happens when the progress indicator is inside a view that was presented as a popover.

Adam Johns
  • 35,397
  • 25
  • 123
  • 176

4 Answers4

5

I had same issue when I use NSProgressIndicator inside NSPopover.

My solution was to set appearance to popover like below.

popover.appearance = NSAppearance(named: NSAppearanceNameAqua)
  • Tried on OSX 10.11.5 / Xcode7.3.1
  • Any other name except for NSAppearanceNameVibrantDark also worked

I'm not sure why this happens, but after setting the property I got correct white background color.

Screenshot: white background progress inside popover

horimislime
  • 106
  • 2
  • 6
4

I've found a way to fix this. The CALayer of the NSProgressIndicator has a sublayer that is showing the tinted background color you are seeing. Hiding that sublayer will cause no background to ever be shown, and keep the spinner visible.

CALayer *layer = [[self popoverProgressIndicator] layer];
CALayer *backgroundLayer = [[layer sublayers] firstObject];
[backgroundLayer setHidden:YES];

I've only seen this issue when inside of an NSPopover where I was using the solution here to change the background.

Community
  • 1
  • 1
IMcD23
  • 445
  • 1
  • 7
  • 13
  • This works for me on macOS Catalina, but only after `NSProgressIndicator ` has been shown. Even `viewDidAppear` is too early for me. – mixtly87 May 25 '20 at 22:49
2

I've encountered this issue recently as well, but mine was related to Dark mode / Light mode in macOS 10.14. It seems to happen when using an NSProgressIndicator inside an NSPopover, as others have mentioned, but also inside a Safari App Extension popover (I think this is because the Safari popover is also an NSPopover that you can't access directly). As horimislime mentioned, messing around with the appearance seems to fix things.

I've created an NSProgressIndicator subclass that, only when the app is running in macOS 10.14 or greater, automatically sets its own appearance to the one the system has, even if this changes while the app is running. The Progress Indicator works for me in previous OS versions, so I left it unchanged if the system is less than 10.14.

Here is the code:

import Cocoa

class TransparentProgressIndicator: NSProgressIndicator {

    override func awakeFromNib() {
        super.awakeFromNib()

        if #available(OSX 10.14, *) {
            // register an observer to detect when the system appearance changes
            UserDefaults.standard.addObserver(self, forKeyPath: "AppleInterfaceStyle", options: .new, context: nil)

            // set the starting appearance to the one the system has
            let light = NSAppearance(named: .aqua)
            let dark = NSAppearance(named: .darkAqua)
            self.appearance = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" ? dark : light
        }
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        if #available(OSX 10.14, *) {
            if keyPath == "AppleInterfaceStyle" {

                // set the current appearance to the one the system has
                let val: String? = change?[NSKeyValueChangeKey.newKey] as? String

                if (val == "Dark") {
                    self.appearance = NSAppearance(named: .darkAqua)
                }
                else {
                    self.appearance = NSAppearance(named: .aqua)
                }
            }
        }
    }

    deinit {
        if #available(OSX 10.14, *) {
            UserDefaults.standard.removeObserver(self, forKeyPath: "AppleInterfaceStyle")
        }
    }

}

This way, the progress indicator stays transparent with no weird tinted background and still changes its color scheme according to the system appearance. In order to use this, you need to add the name of this subclass as the "Custom Class" property in interface builder:

Custom Class example

Now, in order to have a custom background color as requested in the question, you can just have a containing NSBox and add a color to that:

Custom NSBox properties ProgressIndicator constraints

1

I had same issue when I use NSProgressIndicator inside NSPopover.

My solution was to set appearance to the NSProgressIndicator like below.

@IBOutlet weak var progressIndicator: NSProgressIndicator!

override func viewDidLoad() {
    super.viewDidLoad()
    self.progressIndicator.appearance = NSAppearance(named: .aqua)
}

If you prefer you can solve the problem even setting the appearence to the whole popover:

popover.appearance = NSAppearance(named: .aqua)
madx
  • 6,723
  • 4
  • 55
  • 59