Setting IB content filter for NSProgressIndicator color

148 Views Asked by At

I have searched here, Google, Apple Documentation, gitHub, etc. and cannot find any information on how to use the Content Filters to set the color of the NSProgressIndicator. I want to do this in macOS.

I have read this post that talks about modifying the NSProgressIndicator programmatically.

However, I set up a Content Filter, namely False Color, on my NSProgressIndicator in IB. It has two colors, Color 1 and Color 2. I set Color 1 to green, and I set color 2 to red.

By setting the Content Filter, False Color, Color 1 to a custom green color, my NSProgressIndicator is now green by default. So, this tells me it is pulling that color from my Content Filter, Color 1, but I do not know how it is doing that.

How would I set the color of the NSProgressIndicator to Content Filter, Color 2, programmatically?

I would post code on how I am approaching this, but I don't even know how to start.

Another post mentioned setting the .appearance, but that lead me to this repository , which doesn't seem to be what I am looking for. I'm willing to do my homework and figure this out, but I'm coming up empty handed using the Content Filters as set in IB.

EDIT 1: After further investigation, if I perform a print(progressBar.contentFilters) I can then see the two colors that were set in the IB's Content Filter, False Colors settings. This is the printout:

[<CIFalseColor: 0x6000026039c0>
inputImage=nil
inputColor0=<CIColor 0x600000cc4570 (0 0.976805 0 1) devicergb>
inputColor1=<CIColor 0x600000cc45d0 (1 0.149131 0 1) devicergb>
]

So now the question is, how do I make the progressBar use the color that is defined as inputColor1?

EDIT 2: So, after messing around with this for a few more hours, I was able to get a halfway working solution, for now. I say halfway because the call to the progress.contentFilters = [Filter Name] causes a complete Window refresh, which looks like a bright single flicker after the first darkGreen filter is applied in the viewDidLoad method (No flickers occur for the initial setting of the filter).

@IBOutlet weak var progressBar: NSProgressIndicator!

// Set the filter properties
let darkGreenFilter = CIFilter(name: "CIFalseColor")!
let redFilter = CIFilter(name: "CIFalseColor")!
// Set the CIColors for the two filters
let darkGreenCIColor = CIColor(red: 0, green: 0.6, blue: 0.4, alpha: 1)
let redCIColor = CIColor(red: 1, green: 0, blue: 0, alpha: 1)
// Set a boolean flag that the progressBar color was changed
var progressBarColorChanged = false

Then in the viewDidLoad:

// Set the values for the filters
darkGreenFilter.setValue(darkGreenCIColor, forKey: "inputColor0")
redFilter.setValue(redCIColor, forKey: "inputColor0")
// Set the progress indicator to dark green
progressBar.contentFilters = [darkGreenFilter]
// Set the progress to zero (removes the colored bar)
progressBar.doubleValue = 0

Then in a later method that monitors the status of the progressBar

if progressBar.doubleValue > 0.75 && !progressBarColorChanged {
    progressBar.contentFilters = [redFilter]
    progressBarColorChanged = true
}

Then once execution is complete I use a reset method to put it all back

progressBar.contentFilter = [darkGreen]
progressBar.doubleValue = 0
progressBarColorChanged = false

I'm slowly getting there. Anyone have any suggestions on how to do this better, or how I can eliminate that pesky screen flicker?

1

There are 1 best solutions below

0
SouthernYankee65 On BEST ANSWER

Coming back around to this. Here's my answer

Swift 5.4 / macOS

import Cocoa

class ViewController: NSViewController {

    let greenFilter = CIFilter(name: "CIFalseColor")!
    let greenCIColor = CIColor(red: 0.3333, green: 0.8667, blue: 0.0, alpha: 1.0)
    let darkGreenCIColor = CIColor(red: 0, green: 0.5373, blue: 0.1137, alpha: 1.0)
    let redFilter = CIFilter(name: "CIFalseColor")!
    let redCIColor = CIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
    let darkRedCIColor = CIColor(red: 0.6275, green: 0.0, blue: 0.0078, alpha: 1.0)
    var redFilterColorChange = false
    var progressBar: NSProgressIndicator?
    var timer = Timer()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        let progressFrame = NSRect(x: view.frame.midX-view.frame.width/2+30, y: view.frame.midY-10, width: view.frame.width-60, height: 20)
        progressBar = NSProgressIndicator(frame: progressFrame)
        self.view.addSubview(progressBar!)
        progressBar?.contentFilters = []
        if NSApp.effectiveAppearance.name == .darkAqua {
            greenFilter.setValue(darkGreenCIColor, forKey: "inputColor0")
            redFilter.setValue(darkRedCIColor, forKey: "inputColor0")
        } else {
            greenFilter.setValue(greenCIColor, forKey: "inputColor0")
            redFilter.setValue(redCIColor, forKey: "inputColor0")
        }
        // Apply the filter
        progressBar?.contentFilters = [greenFilter]
        progressBar?.isHidden = false
        progressBar?.isIndeterminate = false
        progressBar?.doubleValue = 0
        progressBar?.maxValue = 1.0
        startTimer()
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }

    func startTimer() {
        print("Starting timer")
        timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
        print("Progress bar frame = \(progressBar!.frame)")
    }

    @objc func updateProgress() {
        if progressBar!.doubleValue < 1.0 {
            if progressBar!.doubleValue > 0.75 && !redFilterColorChange {
                progressBar?.contentFilters = [redFilter]
                redFilterColorChange = true
            }
            progressBar?.doubleValue += 0.01
            print("Progress bar value = \(progressBar!.doubleValue)")
        } else {
            timer.invalidate()
            progressBar?.contentFilters = [greenFilter]
            progressBar?.doubleValue = 0
            progressBar?.isHidden = true
        }
    }
}