UIAlertView without Buttons – Please Wait Dialog

Thu, Feb 11

If you’ve ever wanted to show a simple “please wait” dialog without resorting to a custom view, UIAlertView is a good option, and is even more appropriate if you customize the alert such that no buttons are shown.

In the figure below you can see how a simple alert can be shown (sans buttons) while you are busy doing some other system activity (reading/writing files, etc).


UIAlertView without Buttons
UIAlertView *alert;
 
...
 
alert = [[[UIAlertView alloc] initWithTitle:@"Configuring Preferences\nPlease Wait..." 
  message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] autorelease];
 
[alert show];

Trouble with this approach is that things look a little lopsided, as there is a significant amount of dead space on the bottom where the button(s) are to be shown. We can fix this by adding a few newline characters at the start of our message:

UIAlertView *alert;
 
...
 
// Add two newlines characters at the start of the message
alert = [[[UIAlertView alloc] initWithTitle:@"\n\nConfiguring Preferences\nPlease Wait..." 
  message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] autorelease];
 
[alert show];

The text is nearly centered, yet we’ve created a different problem, there is now white space on the top and bottom. There is one more approach…

UIAlertView with UIActivity Indicator

In the whitespace on the bottom, let’s add an activity indicator. Also, remove the newlines in the message text so the text starts near the top:

UIAlertView *alert;
 
...
 
alert = [[[UIAlertView alloc] initWithTitle:@"Configuring Preferences\nPlease Wait..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles: nil] autorelease];
[alert show];
 
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
 
// Adjust the indicator so it is up a few pixels from the bottom of the alert
indicator.center = CGPointMake(alert.bounds.size.width / 2, alert.bounds.size.height - 50);
[indicator startAnimating];
[alert addSubview:indicator];
[indicator release];

Dismissing the Buttonless UIAlertView

Since there are no buttons associated with the alert, we have to dismiss the alert ourselves, versus the traditional approach where the system dismisses the alert when a button is pressed.

Here is the call to dismiss the alert:

[alert dismissWithClickedButtonIndex:0 animated:YES];

28 comments

Wouldn’t it be better to set the delegate to nil? That way you don’t get the callback for the simulated button press which you don’t care about anyway.

by Hendrik on Feb 12, 2010. Reply #

Good call Hendrik, thanks for the tip, that would seem to be a much better approach.

by John Muchow on Feb 13, 2010. Reply #

Love it, looks great :)

by Jarrod N on Mar 7, 2010. Reply #

great tip, helped, thanks!

by cmate on Mar 12, 2010. Reply #

This worked fantastically! Thanks!

by tehp on Mar 17, 2010. Reply #

This is great tutorial, thx.

by jimmy on Jun 9, 2010. Reply #

This works great although the second time this occurs in my app the bounds.size.width info is zero, so the indicator does not get center. I worked around this by storing these values from first attempt (which works great) but that is a hack.

Any ideas?

by ax on Sep 27, 2010. Reply #

ax:
This is also a bit of a “hack” solution, but I think it’s better than storing the first set of width/height values:

- wrap the activity indicator related statements in a method; say “addSpinner”
- after you call [alertView show], do a [self performSelector:@selector(addSpinner) withObject:nil afterDelay:0.05f]

Basically you delay adding the indicator – by an inconsequential amount in human standards – onto the alertview to give time for the bound values to be set.

Hope that helps.

by Vincent K. Armas on Sep 30, 2010. Reply #

Thanks for the hack, works nicely, except for the complaining about the indicator not being centered correctly on first show.

The way I did this was to implement the UIAlertView delegate “didPresentAlertView:”

Note that “busyAlert” is an ivar.

Here’s the code:

- (void)didPresentAlertView:(UIAlertView *)alertView
{
static BOOL busyAlertHasIndicator = NO;

if (alertView != busyAlert || busyAlertHasIndicator)
return;

UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
indicator.center = CGPointMake(busyAlert.bounds.size.width / 2, busyAlert.bounds.size.height – 50);
[indicator startAnimating];
[busyAlert addSubview:indicator];
[indicator release];
busyAlertHasIndicator = YES;

}

by John L on Nov 19, 2010. Reply #

This looks like exactly what I am looking for.

One question though… in a few other places they suggest that modifying the standard alert view can get your app rejected. Has anyone had any problems with this or perhaps could offer further insight into the UI programming guidelines?

Many thanks.

by Gavin on Feb 28, 2011. Reply #

You don’t need to autorelease a UIAlertView. Just release it after you do “show”.

by bob on Apr 27, 2011. Reply #

Just great. Also wondering if there is a way to set the dialog to be on the screen for a minimum amount of time in case the activity we are undertaking is so short that the user can’t read the message …

by Sam Joseph on Jun 6, 2011. Reply #

Great!! Helped a lot!!

by Shashank on Jun 23, 2011. Reply #

I can’t dismiss the alert from XMLParser delegate method :

- (void)parserDidEndDocument:(NSXMLParser *)parser {
[alert dismissWithClickedButtonIndex:0 animated:YES];
[tableView reloadData];
}
the UIAlertView *alert is defined in the header file .

by Ammar on Jul 15, 2011. Reply #

Dude, you rock! this is exactly what i needed.

by Tim C. on Aug 30, 2011. Reply #

great tip! But I didn’t understand how the “dismissWithClickedButtonIndex” accepts a button Index when there are no buttons in the alertView?

by Balaram on Sep 20, 2011. Reply #

To dismiss the alert, you call the method dismissWithClickedButtonIndex of the Alert object and you must provide an index value as a value for the dismissWithClickedButtonIndex parameter.

by John Muchow on Sep 21, 2011. Reply #

using the alert the user has the impression that the app froze. Since the alert is loaded with a second or two delay and the screen became whiter on the center.
The alert is loaded before reading from the web

by Kandy on Nov 15, 2011. Reply #

Is there a way to dismiss the dialog on a delay? Or, put differently, I’d like to use

[alert performSelector:withObject:afterDelay:]

But withObject: only accepts one object, and dismissWithClickedButtonIndex:animated: demands two. I’ve run into this problem before with perform:withObject: when there is more than one argument being passed to the selector.

Any hints?

Hmm. Just had a thought–can I pass an array of objects? Will performSelector grok it and pull the appropriate data? Or maybe a dictionary with the individual selectors as keys? I’ll experiment.

by Gregory Hill on Jan 6, 2012. Reply #

Have a handy helper class that I wrote to do this using blocks:

+ (void) executeBlock:(NMBlockHelperGenericBlock) block afterDelay:(NSTimeInterval) seconds {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[NSThread sleepForTimeInterval:seconds];

dispatch_async(dispatch_get_main_queue(), ^{
block();
});
});

}

by Todd Marshall on Sep 23, 2012. Reply #

Is there a way to stop the UIAlertView and UIProgressIndicator after 3 seconds?

by Manny on Mar 23, 2012. Reply #

UIAlert is a modal dialog, so it must be acknowledged by the user.

by John Muchow on Mar 23, 2012. Reply #

thanks it works

by rizwan on Jun 21, 2012. Reply #

Thanks i looking for something like this, you are the best!!!!

by Daniel on Aug 17, 2012. Reply #

I love it, thank very much for sharing. Please keep up the good work.

by George on Oct 18, 2012. Reply #

To make sure the loading animation is always shown where you want it, without hacks, this will do it:

self.alertView = [[UIAlertView alloc] initWithTitle:@”Your Title Here” message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];

[self.alertView show];

indicator.center = CGPointMake(self.alertView.bounds.size.width / 2, self.alertView.bounds.size.height – 50);
[self.alertView addSubview:indicator];
[indicator startAnimating];

They key is to first show your UIAlertView and then define where you want to put the indicator. You can’t get a view’s bounds before it is visible, though as a user you will never see that the order of these things is reversed.

by Joe Smith on Nov 1, 2012. Reply #

Joe, thanks for the tip.

by John Muchow on Nov 2, 2012. Reply #

Thanks for your great tutorial. :)

by Joe Smith on Nov 3, 2012. Reply #

Leave a Comment