0

Yesterday I added a UIActivityIndicatorView to my iOS application and everything was fine, now I'm trying to run the same application but the UIActivityIndicatorView is not showing anymore and the agenda view (which calls the web service) takes very long time (more than yesterday) to appear and often it does not appear at all. How can I fix this problem, please? Here is my code:

- (IBAction)agenda:(id)sender {

    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    spinner.center = CGPointMake(160, 240);
    spinner.hidesWhenStopped = YES;
    [self.view addSubview:spinner];
    [spinner startAnimating];
    // how we stop refresh from freezing the main UI thread
    dispatch_queue_t downloadQueue = dispatch_queue_create("downloader", NULL);
    dispatch_async(downloadQueue, ^{

        // do our long running process here
        // [NSThread sleepForTimeInterval:10];
        AgendaViewController *agenda = [[ AgendaViewController alloc] initWithNibName:nil bundle:nil];

        // do any UI stuff on the main UI thread
        dispatch_async(dispatch_get_main_queue(), ^{

            [spinner stopAnimating];
            [self.navigationController pushViewController:agenda animated:YES];           
        });

    });
    dispatch_release(downloadQueue);

}
Lyndsey Scott
  • 37,080
  • 10
  • 92
  • 128
  • That code looks alright to me. I would advise commenting out the `queue` code and check if the spinner shows up (do not `stopAnimating`). The first culprit that comes to mind is the "long running process" code that you are not showing. – Callistino May 12 '14 at 16:44
  • When I comment the `queue` code the spinner shows up even by using `[NSThread sleepForTimeInterval:10];` when queue code is uncommented but it doesn't appear when I add this code : `AgendaViewController *agenda = [[ AgendaViewController alloc] initWithNibName:nil bundle:nil];` it seems like the problem caused by this line that is the code I want to add in the " long running process" Any suggestions please ? – user2389273 May 12 '14 at 17:35
  • Try adding the spinner subview on the main thread using a dispatch_async(dispatch_get_main_queue() block. – Lyndsey Scott May 12 '14 at 17:57
  • OK, try to give your `AgendaViewController` an identifier and instantiate your object like that. I assume you are using `storyboards`. Here is an [example](http://stackoverflow.com/a/8853872/2150138). – Callistino May 12 '14 at 19:37
  • No I'm using xib files – user2389273 May 12 '14 at 19:42
  • I don't know why when I do it like this, code works fine: `dispatch_async(downloadQueue, ^{ // do our long running process here [NSThread sleepForTimeInterval:1]; // adding this line makes the uiactivityindicatorview appears AgendaViewController *agenda = [[ AgendaViewController alloc] initWithNibName:nil bundle:nil];` – user2389273 May 12 '14 at 19:52
  • Then you need to pass the `xib` filename to the `initWithNibName:`. Calling `initWithNibName:` with `nil` arguments is the same as just calling `init`. It will create an object directly from the `class` definition but it will not have anything from the `xib` files. – Callistino May 12 '14 at 20:24
  • I believe that when you "uncomment" the `sleepForTimeInterval` line "you think" it works because it has enough time to show the spinner before pushing a "blank" `agenda` VC. – Callistino May 12 '14 at 20:32

2 Answers2

0

Your code looks strange to me.

If AgendaViewController is a UIViewController then it should be touched on the main thread. Hence I would separe code that you uses for retrieve data from your service to the code related to UIViewController.

I would modify the code as follows. This is a possible solution.

// spinner could become a strong property int the presenter controller
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.spinner.center = CGPointMake(160, 240);
self.spinner.hidesWhenStopped = YES;
[self.view addSubview:self.spinner];
[self.spinner startAnimating];

__weak typeof(self) weakSelf = self;

// moved the dispatch_queue into an instance variable or property but do not forget to release
dispatch_async(downloadQueue, ^{

    // call the service here to retrieve serverData

    // do any UI stuff on the main UI thread
    dispatch_async(dispatch_get_main_queue(), ^{

        typeof(self) strongSelf = weakSelf;
        if(strongSelf) {
            AgendaViewController *agenda = [[ AgendaViewController alloc] initWithNibName:nil bundle:nil];
            agenda.serverData = serverData;
            [strongSelf.navigationController pushViewController:agenda animated:YES];
            [strongSelf.spinner stopAnimating];     
        }    
    });

});
Lorenzo B
  • 33,216
  • 24
  • 116
  • 190
0

The problem has to be related to the way you are instantiating your agenda VC. If you are using xib files, then you need to pass the name of the xib to your initWithNibName. Calling initWithNibName: with nil arguments is the same as just calling init. It will create an object directly from the class definition but it will not have anything that you customly created on the xib file.

I believe that when you uncomment the sleepForTimeInterval: line "you think" it works because it has enough time to show the spinner before pushing a blank agenda VC.

Note: The only way the initWithNibName: works with nil arguments (which I still think you should just call init instead), is when the xib file is named after your VC. More info here.

Update

Based on the commenst to this answer, your long running process is not inside the async code but in the viewDidLoad: method inside your AgendaViewController. Therefore, one solution would be:

  1. Remove the async code completely from the action and replace it by just the init/push logic of the AgendaViewController.
  2. Add/start the UIActivityIndicatorView inside your AgendaViewController's viewDidLoad:.
  3. Make your call to the mySQL DB (the actual "long running process").
  4. Stop the spinner/activityIndicator when you receive the data.

I don't know how you are implementing your mySQL call (web service/api) but it should provide either a block or a protocol method that gets called when it is done. That is where you need to add your logic to stop the spinner.

Community
  • 1
  • 1
Callistino
  • 1,075
  • 11
  • 14
  • Thank you Callistino, I changed `nil` by the name of the xib file, it worked fine in the first test and then I tried to run the application again, and same problem returns, the `uictivityindicatorview`disappeared and after few seconds the `agenda`view appears normally, same thing with the code int the question, it works fine yesterday but not now anymore, don't know why ! – user2389273 May 12 '14 at 22:02
  • Then you need to show us what your "long running" process is. I suspect that you are doing some `async` operation that doesn't finish before you try to instantiate the `agenda` VC. If that is the case, you need to use the `async` operation `return/block` to instantiate your `agenda` VC. Then, you will not need **GCD** at all. – Callistino May 13 '14 at 12:53
  • My long running process is to declare the `agenda`view and then push it from the `dispatch_async(dispatch_get_main_queue(), ^{`block (the `agenda`calls data from `mysql`database in it's `viewdidload`method this is why it takes time to load and display its content) so the " long running process " contains just this code : `AgendaViewController *agenda = [[ AgendaViewController alloc] initWithNibName:@"AgendaViewController" bundle:nil]; agenda.lat = lat; agenda.longt = longt; agenda.agendaBackOffice = @"0"; agenda.agendaStatut= @""; agenda.userId = @"";` – user2389273 May 13 '14 at 20:34
  • That is the problem, your are **initialazing** your VC on the the `async` process but the **actual long running process** doesn't occur until the `viewDidLoad:` which in turn doesn't occur until the `VC` is pushed onto the `navigationController`. In other words, your view is displayed (**pushed**) before the data is loaded and way after the `activityIndicatorView` has been stopped. – Callistino May 14 '14 at 12:45
  • Thanx for responding, but how can I fix this problem ? – user2389273 May 16 '14 at 17:38