This article is based on iPad in Practice, to be published on Summer 2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) eBooks and pBooks. MEAPs are sold exclusively through Manning.com. All pBook purchases include free PDF, mobi and epub. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information. [ Use promotional code ‘java40beat’ and get 40% discount on eBooks and pBooks ]
Using an alert view as a task loader
Introduction
Some actions, like signing out of an account in an application, require a user to both confirm the action and to indicate the processing of the action. When you need to present this type of flow to a user, an interesting option is to present an alert with a confirm/cancel option and then to use the alert as a loader while the action is being processed.
Problem
Providing a loader during different types of operations can be a very valuable user interaction technique. One approach to doing so is by providing a loader as part of an alert view interaction.
Solution
Our app is going have one view with a button to fire the alert interaction. When the user taps the button, we display an alert that gives them the choice to sign out of the application. If they tap the action button, we display a loader and process the action. Once the action is completed, we display a second alert view that confirms the completion of the action.
Start by creating a new project. Call it AlertLoader and select a view-based project as the project type. Click on the AlerLoaderViewController.h header file as in listing 1 to start setting up the view controller.
Listing 1 AlertLoaderViewController.h header
#import @interface AlertLoaderViewController : UIViewController { UIAlertView *alertView; } @property(nonatomic, retain) UIAlertView *alertView; #1 - (IBAction) signout; #2 - (void) runProcess; #2 - (void) showAlertLoader; #2 @end #1 UIAlertView property #2 Function and action declarations
In the header file, we define a single property (#1), which is a UIAlertView. This is the alert view we’ll be using to control our interaction and to display the loader. We also declare three functions (#2) on IBAction, which will be attached to our button to start the interaction. The other two we’ll review in the implementation of the controller. Edit your header file to implement the property and function declarations.
Now double-click on the AlertLoaderViewController.xib to modify the UI in the interface builder. Your view will be pretty much empty; all you need to do is drag a single UIButton on the view and give it the title Sign Out. Now, connect the button to the IBAction signout on the controller as shown in figure 1.
The core of the solution is in the implementation of the controller where we implement the alert interactions. Let’s take a look at that next in listing 2.
Listing 2 AlertLoaderViewController.m implementation
#import "AlertLoaderViewController.h" #define START_RUNNING_INDEX 1 #define PROCESS_LOADING_TAG 200 #define PROCESS_LABEL_TAG 201 @implementation AlertLoaderViewController @synthesize alertView; #pragma mark - #pragma mark AlertView Delegate methods (void)alertView:(UIAlertView *)alertView #1 clickedButtonAtIndex:(NSInteger)buttonIndex { #1 if(START_RUNNING_INDEX == buttonIndex) #1 [self runProcess]; #1 } #pragma mark - #pragma mark Sync now - (IBAction) signout { #2 NSLog(@"sync now"); #2 self.alertView = [[[UIAlertView alloc] #2 initWithTitle:@"Sign Out" #2 message:@"Sign Out" #2 delegate:self #2 cancelButtonTitle:@"Cancel" #2 otherButtonTitles:@"Confirm", nil] autorelease]; #2 UIActivityIndicatorView *loading = #2 [[UIActivityIndicatorView alloc] #2 initWithActivityIndicatorStyle: #2 UIActivityIndicatorViewStyleWhiteLarge]; #2 loading.frame = CGRectMake(122.0, 40.0, #2 loading.frame.size.width, #2 loading.frame.size.width); #2 loading.tag = PROCESS_LOADING_TAG; #2 [loading stopAnimating]; #2 [alertView addSubview:loading]; #2 [loading release]; #2 [alertView show]; #2 } #2 - (void) showAlertLoader { #3 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #3 [(UIActivityIndicatorView *) #3 [self.alertView viewWithTag:PROCESS_LOADING_TAG] #3 startAnimating]; #3 [(UILabel *)[self.alertView viewWithTag:PROCESS_LABEL_TAG] #3 setHidden:NO]; #3 self.alertView.title = @"Processing..."; #3 self.alertView.message = @""; #3 [pool release]; #3 } #3 #pragma mark - - (void) runProcess { #4 [NSThread detachNewThreadSelector:@selector(showAlertLoader) #4 toTarget:self #4 withObject:nil]; #4 [NSThread sleepForTimeInterval:10]; #4 UIAlertView *alert = [[[UIAlertView alloc] #4 initWithTitle:@"Completed" #4 message:@"Sign out completed" #4 delegate:nil #4 cancelButtonTitle:@"OK" #4 otherButtonTitles:nil] autorelease]; #4 [alert show]; #4 } #4 - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation { return YES; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)dealloc { [alertView release]; [super dealloc]; } @end #1 Alert view delegate function #2 Display alert and init loader #3 Display the loader #4 runProcess our fake worker function
The AlertLoaderViewController first implements the UIAlertView delegate protocol at #1 by implementing alertView:clickedButtonAtIndex: function, which is called when an alert view button is tapped. In this case, we have a constant START_RUNNING_INDEX, which is used to match the action button index for our main alert view.
At #2, we implement the signout IBAction, which is responsible for displaying the choice alert view to our user. When we create the alert view we also create a UIActivityIndicatorView, which will be our loader, set its frame, and add it as a subview to the alert view. You’ll notice that we call stopAnimating on the activity indicator. The default behavior of the activity indicator is to hide itself when it is not animation. So, we call stopAnimating when we add it and then later call startAnimating to display it. When signout is called, we get the alert view pictured in figure 2.
The showAlertLoader (#3) and runProcess (#4) functions work together. runProcess is actually what is called by our alert view delegate function when the user taps Confirm in the Sign Out alert. The runProcess function first fires off a call to showAlertLoader in the background using detachNewThreadSelector:toTarget:withObject:, which modifies the displayed loader to show our activity indicator, as depicted in figure 3.
Finally, in #4, after the loader has been displayed, we do our fake work. In this case, we just call sleepForTimeInterval: to pause the thread for 10 seconds as a simulation of doing some other work. Once the sleep completes, we create and show a new alert view to inform the user that the process has completed.
Discussion
Looking at this technique, we can see that it’s fairly simple; all it really does is add a subview to an alert view as a means of smoothing out some user interaction flow. It’s worth noting that this isn’t something to take too lightly. Adding a subview to a system view is accepted by Apple and I’ve used this approach in a few different applications in cases where it made sense. However, you need to be aware that trying to use private properties or views of a system view is a different story. I’ve heard it directly from Apple engineers that they not only frown upon trying to access and manipulate private components but that they are going to be checking for this and rejecting apps based on it. This particular technique doesn’t try to use or change the original UIAlertView other than clearing the text of the detail label, but going any farther may be walking a fine line with the app review team.
As shown in figure 4, after our loader has completed we show a confirmation loader, this is an optional extra piece of information you can show the user.
Summary
Alerts are a simple view, which was carried over from the iPhone SDK they give you a way to notify users of changes in state. You should try to use Alerts only in cases where it is really necessary to notify the user of a change that will impact their use of the application. Alerts views are extremely simple but can be very effective so it is a good idea to be familiar with how to use them.
We showed you how to set up one view with a button to fire the alert interaction so that, when the user taps the button, we display an alert that gives them the choice to sign out of the application. Tapping on the action button displayed a loader and processed the action at the same time. Once the action was completed, we displayed a second alert view that confirmed the completion of the action.