9

I want to use an NSProgressIndicator to show some progress. But I couldn't find a method which increases the bar of that control. I did also not find an example on the web.

let progressbar = NSProgressIndicator()
progressbar.frame = NSRect(x: 100, y: 20, width: 150, height: 10)
progressbar.minValue = 0
progressbar.maxValue = 10
self.window.contentView?.addSubview(progressbar)

for i in 0...10 {
    progressbar.incrementBy(1)  //has no effect
}
PjotrC
  • 311
  • 1
  • 6
  • 16
  • What is appearing on screen? Is it indeterminate, circular, or otherwise incapable of showing progress? Or is it a determinate bar that just isn't showing any progress? – Tommy Jul 05 '16 at 21:20
  • 1
    @PjotrC read the documentation https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSProgressIndicator_Class/index.html – Abhinay Reddy Keesara Jul 05 '16 at 21:21
  • @Tommy Yes, it is a determinate bar which isn't showing any progress (it stays grey). – PjotrC Jul 05 '16 at 21:28

2 Answers2

10

You won't be able to demonstrate a progress bar in such a tight loop like that.

When you set the value of the progress indicator, the OS X display mechanism won't actually draw the difference until the next time through the event loop, which doesn't happen until after your method returns. In other words, you're setting it all the way to 10 before the progress indicator even gets a chance to redraw itself, so all you see is the final filled state.

You could theoretically force the progress indicator to display after each time through the loop (progressbar.display()), but I don't think you'd be able to distinguish the differences when they happen within, say, 0.01 seconds.

The solution then, is to introduce a small delay between calling incrementBy(1), so that the next event loop can happen and the display of the new value will take place. You can do that by adding something like the following to your code:

func delay(delay:Double, closure:()->()) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
        Int64(delay * Double(NSEC_PER_SEC))),
                   dispatch_get_main_queue(), closure)
}

class AppDelegate: NSObject, NSApplicationDelegate {
    @IBOutlet weak var progressIndicator: NSProgressIndicator!

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        let progressbar = NSProgressIndicator()
        progressbar.frame = NSRect(x:100, y:20, width:150, height:10)
        progressbar.minValue = 0
        progressbar.maxValue = 10
        self.window.contentView?.addSubview(progressbar)

        self.progressIndicator = progressbar
        progressIndicator.indeterminate = false

        for i in 0...10 {
            delay(1.0 * Double(i), closure: { 
                self.progressIndicator.incrementBy(1)
            })
        }
    }
}

This queues up the calling of incrementBy(1) to be at 1 second intervals.

NSGod
  • 22,699
  • 3
  • 58
  • 66
  • 3
    `progressbar.indeterminate = false` was missing in my code... I am downloading files in my loop, so the delay is not needed here. But I will remember your example for use in the future. Thanks a lot. – PjotrC Jul 05 '16 at 22:56
  • Programmatically initializing & setting NSProgressIndicator properties does not work. Has anybody faced this issue? – K_Mohit Sep 16 '21 at 14:01
0

In my case. the progress did not work. adding :

self.progress_bar.indeterminate = false;

fix the problem.

user1105951
  • 2,259
  • 2
  • 34
  • 55