0

This question has been asked before, but the answers aren't working. I know that (correct me if I'm wrong) the main thread updates the UI, so we need to call the main thread in the loop. I'm trying this and the progress bar only updates at the end of the loop (so goes from 0% to 100%)

here is my code

.h

@interface ViewController : UIViewController <UITextFieldDelegate> {
IBOutlet UIProgressView *progressBar;
float progressBarPercentage;
}
-(IBAction)button:(id)sender;

@end

.m

-(IBAction)button:(id)sender{

for (int z=0; z < 9; z++){

//do stuff

float progressBarPercentage = (1.0 / 9.0 * (z + 1));
        [self performSelectorOnMainThread:@selector(makeMyProgressBarMove) withObject:nil waitUntilDone:NO];

}

}

-(void)makeMyProgressBarMove{

    [progressBar setProgress:progressBarPercentage animated:YES];
     }

When running in debug mode, I've noticed when it gets to the line [self performSelectorOnMainThread:@selector(makeMyProgressBarMove) withObject:nil waitUntilDone:NO]; it just restarts the loop without going to 1makeMyProgressBarMove:`

Also, the //do stuff part isn't short, it actually takes 5s to run the code when the button is pressed, so it's not like it's updating so fast I can't see it.

Thank you, and just let me know if more information is needed, I'm still a beginner

uti0mnia
  • 394
  • 6
  • 19
  • 1
    You need to "Do Stuff" on another thread also, otherwise it will still block your main thread. Note that `performSelectorOnMainThread` has no use if you're already on the main thread. – Taum Jan 29 '14 at 16:06
  • I'm on ViewController.m, is that a main thread? – uti0mnia Jan 29 '14 at 16:07
  • Is progressBarPercentage declared inside IBAction or outside it? – Piyuesh Jan 29 '14 at 16:08
  • I forgot to add that, I will edit but it's declared in my .h file as a float – uti0mnia Jan 29 '14 at 16:09
  • 1
    Being on the main thread or not is not related to which file you're in or which object your method runs on, but rather how the method is called. Here your method is running on the main thread because it's called from a button from your UI (everything UI-related is always done on the main thread). – Taum Jan 29 '14 at 16:16
  • Also your `button:` method creates a local variable called `progressBarPercentage` which shadows the instance variable. Therefore the instance variable is always 0. I'd recommend dropping the instance variable and using blocks, see my solution. But if you want to keep using an instance variable, you need to remove the `float` before `progressBarPercentage` so that it refers to the instance variable instead. – Taum Jan 29 '14 at 16:18
  • I don't think so, the variable `progressBarPercentage` is declared in the header file and when looking at the code in the debugger, when it goes to the `meMyProgressBarMove:` method it knows what the value of `progressBarPercentage` is, but still doesn't update my UIProgressView – uti0mnia Jan 29 '14 at 16:21

2 Answers2

0

You need to "Do Stuff" on another thread also, otherwise it will still block your main thread. Note that performSelectorOnMainThread has no use if you're already on the main thread.

Using libdispatch because it's much easier:

-(IBAction)button:(id)sender{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (int z=0; z < 9; z++)
        {
            //do stuff

            float progressBarPercentage = (1.0 / 9.0 * (z + 1));

            dispatch_async(dispatch_get_main_queue(), ^{
                [progressBar setProgress:progressBarPercentage animated:YES];
            });
        }
    });
}
Taum
  • 2,511
  • 18
  • 18
  • Yes, sorry. That should be fixed! – Taum Jan 29 '14 at 16:18
  • Okay so it works... Can you explain why? what is the `dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)` stuff? – uti0mnia Jan 29 '14 at 16:23
  • Queues are abstractions of threads that make them much easier to use IMO. Think of them as something able to execute arbitrary blocks of code in FIFO order. **dispatch_async** is a function that will submit a block to a queue for execution, and return immediately. Therefore your `button:` method returns and does not block your main thread. In the meantime, the block sent to (the first) dispatch_async starts executing, and periodically calls back to the main thread with another dispatch_async to update your progress bar. – Taum Jan 29 '14 at 16:28
  • If you want to understand in more depth why blocking the main thread is a problem and how dispatch queues solve this problem, you should read about run-loops (a very important concept), threads and dispatch queues. I'm sure there are a lot of articles out there than will explain it in much clearer terms than I can :) – Taum Jan 29 '14 at 16:30
  • thank you very much! I will read more. I'm not in any computer related field so all the concept things are very new to me! Thanks again Taum! :D – uti0mnia Jan 29 '14 at 16:34
-1

now try this

 -(IBAction)button:(id)sender{

    for (int z=0; z < 9; z++){

    //do stuff

    float progressBarPercentage = (1.0 / 9.0 * (z + 1));
            [self performSelectorOnMainThread:@selector(makeMyProgressBarMove) withObject:nil waitUntilDone:YES];

    }

    }

    -(void)makeMyProgressBarMove{

        [progressBar setProgress:progressBarPercentage animated:YES];
         }
Sport
  • 8,570
  • 6
  • 46
  • 65
  • I just checked the debugger, and it's moving to the `makeMyProgreeBarMove:` method, but it's still not updating the progressbar – uti0mnia Jan 29 '14 at 16:06