objective-ccocoafor-loopdispatch-asyncnsprogressindicator

NSProgressIndicator doesn't update until the end of the loop


I'm making an app that does a very large amount of calculation for multiple files, and so to keep track of everything that's going on, I added an NSProgressIndicator (values 0-100).

I also have a console in the app, so the logConsole: method writes to that console.

My loop looks like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){

    for(int i = 0; i < _files.count; i++)
    {

        //Do calculations

        dispatch_async(dispatch_get_main_queue(), ^(void){

            [_progressBar setDoubleValue: ((i+1) / _files.count) * 100];
            [self logConsole:[NSString stringWithFormat:@"Completed file #%d", (i+1)]];

        });

    }

});

When this loop runs, the messages are logged to the app's console (not NSLog, the actual GUI console I made) asynchronously, but the progress bar doesn't change until the entire for-loop completes.

So if there were 5 files it would look like this:

LOG: Completed file #1
Progress bar at 0
LOG: Completed file #2
Progress bar at 0
LOG: Completed file #3
Progress bar at 0
LOG: Completed file #4
Progress bar at 0
LOG: Completed file #5
Progress bar at 100

Why isn't the progress bar updating? It's running on the main thread.


Solution

  • It looks like you are doing integer math, which will never result in a floating point value. You will have to cast your values as a double for this to do what you want.

    double progress = (((double)i) + 1.0) / ((double)_files.count);
    [_progressBar setDoubleValue:progress * 100.0];
    

    It's also worth mentioning that you wouldn't have to multiply by 100.0 if you set the minValue and maxValue of the progress bar appropriately (the default is 0.0 and 100.0). You would want to put this in viewDidLoad most likely:

    [_progressBar setMinValue:0.0];
    [_progressBar setMaxValue:1.0];