How do I update a progress bar in Cocoa during a long running loop?

后端 未结 4 505
悲&欢浪女
悲&欢浪女 2020-12-29 00:17

I\'ve got a while loop, that runs for many seconds and that\'s why I want to update a progress bar (NSProgressIndicator) during that process, but it updates onl

相关标签:
4条回答
  • 2020-12-29 00:33

    This worked for me, which is a combination of answers from others that did not seem to work (for me at least) on their own:

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
      //do something
      dispatch_async(dispatch_get_main_queue(), ^{
        progressBar.progress = (double)x / (double)[stockList count];            
      });  
      //do something else
    });
    
    0 讨论(0)
  • 2020-12-29 00:38

    If you are building for Snow Leopard, the easiest solution is in my opinion to use blocks and Grand Central Dispatch.

    The following code shows you how your startIt: method would look like when using GCD.

    Your stopIt: method should work fine as you wrote it. The reason why it wasn't working before is that mouse events happen on the main thread and thus the button didn't respond to you because you were doing work on the main thread. This issue should have been resolved now as the work has been put on a different thread now with GCD. Try the code, and if it doesn't work, let me know and I will see if I made some errors in it.

    // This method runs when a start button is clicked.
    - (IBAction)startIt:(id)sender {
    
        //Create the block that we wish to run on a different thread.
        void (^progressBlock)(void);
        progressBlock = ^{
    
        [progressbar setDoubleValue:0.0];
        [progressbar startAnimation:sender];
        running = YES; // this is a instance variable
    
        int i = 0;
        while (running) {
            if (i++ >= processAmount) { // processAmount is something like 1000000
                running = NO;
                continue;
            }
    
            // Update progress bar
            double progr = (double)i / (double)processAmount;
            NSLog(@"progr: %f", progr); // Logs values between 0.0 and 1.0
    
            //NOTE: It is important to let all UI updates occur on the main thread,
            //so we put the following UI updates on the main queue.
            dispatch_async(dispatch_get_main_queue(), ^{
                [progressbar setDoubleValue:progr];
                [progressbar setNeedsDisplay:YES];
            });
    
            // Do some more hard work here...
        }
    
        }; //end of progressBlock
    
        //Finally, run the block on a different thread.
        dispatch_queue_t queue = dispatch_get_global_queue(0,0);
        dispatch_async(queue,progressBlock);
    }
    
    0 讨论(0)
  • 2020-12-29 00:43

    I believe, my loop prevents other things of that application to happen.

    Correct. You need to break this up somehow.

    One way would be a timer, with you whittling away the queue a little at a time in the timer callback. Another would be to wrap the code to handle one item in an NSOperation subclass, and create instances of that class (operations) and put them into an NSOperationQueue.

    Does this have to do with threads or something?

    Not necessarily. NSOperations run on threads, but the NSOperationQueue will handle spawning the thread for you. A timer is a single-threaded solution: Every timer runs on the thread you schedule it on. That can be an advantage or a disadvantage—you decide.

    See the threads section of my intro to Cocoa for more details.

    0 讨论(0)
  • 2020-12-29 00:49

    You can try this code ..

    [progressbar setUsesThreadedAnimation:YES];
    
    0 讨论(0)
提交回复
热议问题