Question

I have a UIViewController that is presented using presentViewController:animated:completion:. Before being dismissed, it needs to perform some calculations that take a few seconds to finish, so I wanted to use a UIActivityIndicator to display some sort of feedback so user does not think the app is stuck.

I saved the activity indicator into a property just to make it easier to trace while changing the flow of the methods. Ideally, when the user taps the "close" button, the following is executed:

- (void) saveAndClose {
    [self.activityIndicator startAnimating];
    [self performNecessaryCalculations]; // this takes a little while and returns only when finished
    [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}

Yet nothing actually changes on the screen. It's not an issue with activity indicator visibility or functionality, because if I run:

- (void) saveAndClose {
    [self.activityIndicator startAnimating];
}

It animates just fine. Seems that the view does not even attempt to redraw anything before end of the method is reached. How can I achieve this desired sequence of animating, calculating, and dismissing from a single button click?

Edit: I have tried the suggested dispatch_async approach, but it's not yet functioning as desired.

[self.activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self performNecessaryCalculations];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    });
});

The code above shows the activity indicator correctly. However, performNecessaryCalculations method includes a call to tableView reloadData of an offscreen view controller. That's the controller that becomes visible when the controller in question is dismissed. The tableView does not get properly refreshed right away, but only after the user attempts interacting with it (scrolling), or it also gets refreshed after a minute or so of just sitting there (refreshed itself while I was typing all this).

Any tips?

Was it helpful?

Solution 2

I've used performSelector:withObject:afterDelay in similar cases:

- (void) saveAndClose {
  [self.activityIndicator startAnimating];
  [self performSelector:@selector(reallySaveAndClose) withObject:nil afterDelay:0.001];
}

- (void) reallySaveAndClose {
  [self performNecessaryCalculations]; // this takes a little while and returns only when finished
  [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}

This way, everything remains on the main thread, but because of the tiny delay the UI will update in between, so the indicator will show.

OTHER TIPS

Try something like this:

[self.activityIndicator startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self performNecessaryCalculations];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.activityIndicator stopAnimating];
        [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
    });
});

Your calculations are blocking the main thread, so the ActivityIndicator will never be drawn or animated. Spin off the calculations in a separate thread and have it signal the main thread when it's finished.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top