Reputation: 628
I would like to show the progress bar in my app as determinate rather than indeterminate. It doesn't work though when setting it up as determinate (works just fine for indeterminate). I've read some of the other answers to this, although they haven't worked. Any help would be appreciated - thanks!
@interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSProgressIndicator *showProgress;
}
- (IBAction)someMethod:(id)sender {
[showProgress setUsesThreadedAnimation:YES]; // This works
[showProgress startAnimation:self]; // This works
[showProgress setDoubleValue:(0.1)]; // This does not work
[showProgress setIndeterminate:NO]; // This does not work
[self doSomething];
[self doSomethingElse];
[self doSomethingMore];
....
[barProgress setDoubleValue:(1.0)]; // This does not work
[barProgress stopAnimation:self]; // This works
}
Updated code [working]:
- (IBAction)someMethod:(id)sender {
[showProgress setUsesThreadedAnimation:YES];
[showProgress startAnimation:self];
[showProgress setIndeterminate:NO];
[showProgress setDoubleValue:(0.1)];
[showProgress startAnimation:nil];
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
for (NSUInteger i = 0; i < 1; i++) {
dispatch_async(dispatch_get_main_queue(), ^{
[barProgress incrementBy:10.0];
});
}
[self doSomething];
[showProgress incrementBy:...];
dispatch_async(dispatch_get_main_queue(), ^{
[showProgress stopAnimation:nil];
});
});
[showProgress setDoubleValue:(1.0)];
}
Upvotes: 4
Views: 4401
Reputation: 21373
Your doSomething
method is blocking the main thread, which causes the run loop not to cycle, which in turn causes UI redraw to be blocked. The fix is to do the long running work in doSomething
on a background queue, with periodic callbacks to the main queue to update the progress bar.
I have no idea what your doSomething
method does, but for the sake of explanation, let's assume it runs a for loop with 100 steps. You'd implement it something like this:
- (void)doSomething
{
[showProgress startAnimation:nil];
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(backgroundQueue, ^{
for (NSUInteger i = 0; i < 100; i++) {
// Do whatever it is you need to do
dispatch_async(dispatch_get_main_queue(), ^{
[showProgress incrementBy:1.0];
});
}
// Done with long running task
dispatch_async(dispatch_get_main_queue(), ^{
[showProgress stopAnimation:nil];
});
});
}
Keep in mind, you still need to set the progress indicator up to be determinate, initialize its value and set an appropriate minValue and maxValue.
If you must do the work in doSomething
on the main thread, it's possible to schedule small chunks of that work to be done on each run loop cycle, or to manually spin the run loop periodically as you're doing the work, but Grand Central Dispatch (GCD) would be my first choice if you can use it.
Upvotes: 4