3

I am loading data from an API and am using a UIProgressView to display how much has loaded.

In my viewWillAppear I use Reachability to check that there is an internet connection. Then, if there is, the following line is called 10 times in a function.

[self performSelectorInBackground:@selector(updateProgress) withObject:nil];

This then runs this method

-(void)updateProgress {
    float currentProgress = myProgressBar.progress;
    NSLog(@"%0.2f", currentProgress);
    [loadingProg setProgress:currentProgress+0.1 animated:YES];
}

The float increments by 0.1 and the loading view displays this.

When the view is dismissed (it is a modal view) and then recalled, the method runs and the NSLog shows that the currentProgress is incrementing as it should. However, the progress bar remains empty. Does anyone know what could cause this?

For reference, I am using ARC.

Update:

This is how I am calling the API

NSString *urlString = **url**;
NSURL *JSONURL = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:JSONURL
                        cachePolicy:NSURLRequestReloadIgnoringCacheData 
                        timeoutInterval:10];
if(connectionInProgress) {
    [connectionInProgress cancel];
}
connectionInProgress = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];

//This is where I call the update to the progress view

And I have these functions:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    JSONData = [NSMutableData data];
    [JSONData setLength:0];
}

 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [JSONData appendData:data];
}

-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    //add data to mutable array and other things
}
Patrick
  • 6,495
  • 6
  • 51
  • 78
  • There is something I didn't understand in your question, you will update the progress based on data read from the URL Connection or from the disk of your device? – antf Sep 02 '12 at 11:28

2 Answers2

7

When you are dealing with User Interface (UI) components, you must perform the methods in the main thread. As a rule when you program, you need to set UI operations in the main thread, and heavy, complex and more performance demanding operations in background threads - this is called multithreading (as a side suggestion, would be good to read about GCD - Grand Central Dispatch. If you need to do longer operations, check this good tutorial from Ray Wenderlich.)

To solve this, you should call [self performSelectorOnMainThread:@selector(updateProgress) withObject:nil]; and then, in the method, the following:

-(void)updateProgress {
    float currentProgress = myProgressBar.progress;
    NSLog(@"%0.2f", currentProgress);
    dispatch_async(dispatch_get_main_queue(), ^{
    [loadingProg setProgress:currentProgress+0.1 animated:YES];
    });
}
Natan R.
  • 5,141
  • 1
  • 31
  • 48
  • Ahh, I see. This view may take a lot more work than I expected then! The problem with `[self updateProgress]` is that it doesn't do anything until after everything has loaded. – Patrick Sep 02 '12 at 11:04
  • Edited the answer, check it out. – Natan R. Sep 02 '12 at 11:04
  • Hmm, I tried `[self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];` but again it works after the loading is complete. Do you think I should perform the loading in the background and the UI in the main thread? – Patrick Sep 02 '12 at 11:11
  • I don't know what the loading that you are talking about is. If you could post more code would be helpful. – Natan R. Sep 02 '12 at 11:14
  • Try the following: `dispatch_async(dispatch_get_main_queue(), ^{ [loadingProg setProgress:currentProgress+0.1 animated:YES]; });` – Natan R. Sep 02 '12 at 11:22
  • Does this line go after my connectionInProgress line? I can't get it to work – Patrick Sep 02 '12 at 11:34
  • Well, it works great...but only if I call my loading method like `[self performSelectorInBackground:@selector(loadProducts) withObject:nil];` instead of `[self loadProducts];` But then my connectionDidFinishLoading doesn't get called – Patrick Sep 02 '12 at 11:41
  • It does help a lot, now I just need to work out how to call the connectionDidFinishLoading when the main loading is one a background thread – Patrick Sep 02 '12 at 11:45
2

UI refreshes need to happen on the main thread. Change

[self performSelectorInBackground:@selector(updateProgress) withObject:nil];

to

[self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Hi, I tried this line but it doesn't seem to work. When I change waitUntilDone to YES the NSLog shows the update, but again, the progress bar does not change – Patrick Sep 02 '12 at 12:38