Chapter 11
Activity Progress and Alerting Users

Key Skills & Concepts

• Using a UIActivityIndicatorView to indicate processing

• Using a UIProgressView to indicate processing

• Using a UIAlertView to warn users

• Using a UIActionSheet to inform users

• Using application badges to remind users of items needing their attention

While processing, many times an application must inform users that they must wait. A poorly constructed application provides no graphical clue that the application is busy processing; a well-constructed application does provide a graphical clue. The iOS SDK provides the UIActivityIndicatorView and UIProgressView classes to tell a user to “please wait, I’m working.” The UIActivityIndicatorView uses a spinning “gear” to tell a user an application is processing and that it will eventually complete. The UIProgressView control also tells a user to “please wait,” but it provides a visual clue as to how much processing remains. The two controls’ names highlight their difference: The activity indicator shows activity, while the progress view shows progress.

A user-friendly application also informs a user when something unexpected occurs, and it informs a user when his or her decision might be potentially damaging. Moreover, the application informs the user in a way that highlights the importance of the problem. Sometimes unexpected events occur or a user makes a potentially destructive decision. For these situations, an application presents an alert dialog. Alerts provide information in a box separate from the underlying interface. This separation reinforces the alert’s message that the situation is important and unusual, separate from an application’s typical functionality. Alerts are also modal, meaning a user can do nothing else until clicking one of the alert’s buttons to release it. Action sheets are similar to alerts but provide alternatives to actions and slide in from an application’s top (desktop OS X applications) or from an application’s bottom (iOS applications). You use action sheets for similar situations to an alert, but action sheets are more appropriate for actions that are a planned part of application activity.

Showing Activity—the UIActivityIndicatorView

A UIActivityIndicatorView class creates an animated indeterminate progress indicator. This control tells the user to “please wait, I’m processing.” The control does not tell the user how long he or she must wait. Apple’s reference for this class, which refers to the visual element as an animated “gear,” illustrates an activity indicator in use (Figure 11-1). When I start the Amazon.com application, it fetches my content from its web server. Fetching this content takes time—how much time, the application doesn’t know—and so the application uses a UIActivityIndicatorView.

Using a UIActivityIndicatorView in an application is easy. Begin the indicator’s animation by calling its startAnimating method, and stop the indicator’s animation by calling the stopAnimating method. If you wish to hide the indicator when not animated, set the property hidesWhenStopped to YES. You can also specify the activity indicator’s size and style using its activityIndicatorViewStyle property. The indicator types are large white (UIActivityIndicatorViewStyleWhiteLarge), white (UIActivityIndicatorViewStyleWhite), and gray (UIActivityViewStyleGray). Figure 11-2 illustrates setting this property in Interface Builder. Figure 11-3 shows the three styles in iPhone Simulator.

Image

Figure 11-1 An activity indicator on Amazon.com’s application

Image

Figure 11-2 Setting a UIActivityIndicatorView’s style

Image

Figure 11-3 Three different UIActivityIndicatorView styles

Try This
Using a UIActivitylndicatorView

1. Create a new View-based Application. Name the application ActivityAndProgress.

2. Open ActivityAndProgressViewController, add an IBOutlet for a UIActivityIndicatorView, and add an IBAction called do it (Listings 11-1 and 11-2). Save and build.

Listing 11-1 ActivityAndProgressViewController.h

@interface ActivityAndProgressViewController : UIViewController {
 IBOutlet UIActivityIndicatorView * myActivityView;
}
@property (nonatomic, retain) IBOutlet
             UIActivityIndicatorView *myActivityView;
-(IBAction) doIt: (id) sender;
@end

Listing 11-2 ActivityAndProgressViewController.m

#import "ActivityAndProgressViewController.h"
@implementation ActivityAndProgressViewController
@synthesize myActivityView;
-(IBAction) doIt: (id) sender {
  if( [myActivityView isAnimating])
    [myActivityView stopAnimating];
 else
   [myActivityView startAnimating];
}
-(void)dealloc {
 [myActivityView release];
 [super dealloc];
}
@end

3. Open ActivityAndProgressViewController.xib.

4. Drag a button and activity indicator view from the library to the view’s canvas (Figure 11-4).

5. Connect the File’s Owner do it action to the button’s Touch Up Inside event. Connect the myActivityView outlet to the activity indicator view added to the canvas (Figure 11-5).

6. Select the activity indicator view, and open its view attributes in the Inspector. Ensure the indicator’s Hides When Stopped and Hidden check boxes are checked (Figure 11-6).

7. Click Run to view the application in the iPhone Simulator.

8. Click the button two times (Figure 11-7).

Image

Figure 11-4 Adding a button and activity indicator to a view’s canvas

Image

Figure 11-5 Connecting the myActivityView outlet to the activity indicator

Image

Figure 11-6 Ensuring the indicator’s Hides When Stopped and Hidden check boxes are checked

Image

Figure 11-7 The application running in iPhone Simulator

When the application loads, it hides the activity indicator, as it is not animating. When you first click the button, the application displays the activity indicator and begins animating it. The next time you click the button, the application stops animating the indicator and hides it.

Showing Progress—the UIProgressView

A progress bar shows a task’s progress. It is intended as a “please wait, I’m processing and I have this much processing remaining” for tasks with a known duration. For instance, an application might process a file’s content, and as the file is processing, the application calculates the percentage remaining and displays it using a progress bar. As the file’s content is processed, the progress bar updates its display to reflect the new percentage remaining until completion.

Creating a progress bar is more involved than creating an activity indicator. However, it is still not difficult. Before beginning this task, note that a common technique in many books and online tutorials is to set a progress view in an alert. Although it’s easy, as of this book’s writing, Apple neither supports nor recommends this technique. The recommended way of displaying a UIProgressView is by creating a new view with a transparent background and showing the progress bar in the new view. Because the view overlays the content view, the progress bar is modal. That is the strategy taken here.

Try This
Using a UIProgress View

1. Open the ActivityAndProgress project from the previous task.

2. Create a new UIViewController named PleaseWaitViewController. Ensure that it also creates an associated xib.

3. Add an IBOutlet for a UIProgressView to PleaseWaitViewController (Listings 11-3 and 11-4).

Listing 11-3 PleaseWaitViewController.h

#import <UIKit/UIKit.h>
@interface PleaseWaitViewController : UIViewController {
 IBOutlet UIProgressView * myProgress;
}
@property (nonatomic, retain) IBOutlet UIProgressView * myProgress;
@end

Listing 11-4 PleaseWaitViewController.m

#import "PleaseWaitViewController.h"
@implementation PleaseWaitViewController
@synthesize myProgress;
-(void)dealloc {
 [myProgress release];
 [super dealloc];
}
@end

4. Drag starcopy.png from the book’s Resources folder to the Resources folder in Xcode.

5. Build and then open PleaseWaitViewController.xib in Interface Builder.

6. Add an image view from the library to the view’s canvas. Select starcopy.png as the image and set the mode to Scale To Fill if it didn’t default to that.

7. Add a label and a progress view from the library to the view’s canvas (Figure 11-8). Set the current value of the progress bar to 0.

Image

Figure 11-8 PleaseWaitViewController’s view in Interface Builder

8. Connect the UIProgressView on the canvas to the myProgress outlet in PleaseWaitView Controller.

9. Save your changes.

10. Open ActivityAndProgressController.h, add a forward reference to the PleaseWaitView Controller class, and change the myActivityView IBOutlet to use the new class. Add a method called moveBar. Listing 11-5 contains the completed ActivityAndProgressController.h.

Listing 11-5 ActivityAndProgressViewController.h modified for this task

#import <UIKit/UIKit.h>
@class PleaseWaitViewController;
@interface ActivityAndProgressViewController : UIViewController {
    PleaseWaitViewController * myActivityView;
}
@property (nonatomic, retain) IBOutlet PleaseWaitViewController *
myActivityView;

-(void) moveBar: (id) object;
-(IBAction) doIt: (id) sender;
@end

11. Open ActivityAndProgressController.m and implement the methods to match Listing 11-6. Note that do it has changed from the previous task.

Listing 11-6 ActivityAndProgressViewController.m modified for this task

#import "ActivityAndProgressViewController.h"
#import "PleaseWaitViewController.h"
#import "ActivityAndProgressAppDelegate.h"
@implementation ActivityAndProgressViewController
@synthesize myActivityView;

int completed = 0;
-(void) moveBar: (id) object {
 completed ++;
 myActivityView.myProgress.progress = completed/20.0f;
 if(completed > 20) {
   [object invalidate];
   [self.myActivityView.view removeFromSuperview];
   [self.view setAlpha:1.0f];
   completed = 0;
   self.myActivityView.myProgress.progress = 0;
 }
}

-(IBAction) doIt: (id) sender {
 myActivityView.view.backgroundColor = [UIColor clearColor];
 [self.view setAlpha:0.7f];
 [((ActivityAndProgressAppDelegate *)
     [UIApplication sharedApplication].delegate).window
     insertSubview:myActivityView.view aboveSubview: self.view];
 [NSTimer scheduledTimerWithTimeInterval: 0.5 target: self
     selector: @selector(moveBar:) userInfo: nil repeats: YES];
}
-(void)dealloc {
 [myActivityView release];
 [super dealloc];
}
@end

12. Open ActivityAndProgressController.xib in Interface Builder and add several controls (Figure 11-9). The selection is not important, as they are not used.

Image

Figure 11-9 ActivityAndProgressViewController’s canvas

13. Add a view controller from the library to the document window. Change the controller’s class to PleaseWaitViewController. Also, change the controller’s NIB name to PleaseWaitViewController.

14. Connect the File’s Owner myActivityView outlet to the newly added controller.

15. Save your changes and click Run. After the application loads in the iPhone Simulator (Figure 11-10), click the button. The PleaseWaitViewController’s view is displayed and the progress view updates (Figure 11-11). Upon completion, the application removes the view and displays the original view once again.

Image

Figure 11-10 The application running in iPhone Simulator

Image

Figure 11-11 The application displaying the UIProgressView you added to the project

This task is pretty cool. You first created a separate view controller and view to contain the UIProgressView. Not happy with just a normal view, you added an image and placed the controls over the image. Later, because the view’s background color is set to clear, this created the impression of a non-rectangular view (see Figure 11-8).

Image

Figure 11-12 A simple non-rectangular view

You also added a UIProgressView to the PleaseWaitViewController (Listing 11-3) as an outlet. You then added a PleaseWaitViewController (Listing 11-5) as an outlet of ActivityAndProgress ViewController. This allows you to reference both the PleaseWaitViewController and its UIProgressView from code in ActivityAndProgressViewController.

In ActivityAndProgressViewController, you created a method called moveBar and modified the do it method. The do it method is called when a user clicks the button on the ActivityAndProgressViewController. The method first sets its myActivity view’s background color to clear, making the view transparent.

myActivityView.view.backgroundColor = [UIColor clearColor];

The method then sets the view’s alpha level, giving it a faded, semitransparent appearance.

[self.view setAlpha:0.7f];

The do it method also then gets a reference to the application’s window and adds myActivity’s view above the ActivityAndProgressViewController’s view.

 [((ActivityAndProgressAppDelegate *)
    [UIApplication sharedApplication] .delegate).window insertSubview:
    myActivityView.view aboveSubview: self.view];

To simulate a long-standing task, you use an NSTimer, which every half a second calls ActivityAndProgressViewController’s moveBar method.

[NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector:
     @selector(moveBar:) userInfo: nil repeats: YES];

The moveBar method updates the progress bar until it reaches 20. It then invalidates the timer, removes PleaseWaitViewController’s view, and sets the original view’s alpha back to full strength. It also reinitializes the UIProgressView’s progress value to zero.

[NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector:
     @selector(moveBar:) userInfo: nil repeats: YES];

Alerting Users

You should use alerts to inform users of impending actions that might be destructive, “are you sure” types of messages. Usually these are a result of some unexpected action. Alerts should have two buttons: an “okay” button and a “cancel” button. Alerts can also be used to notify the user that something very important just happened that wouldn’t otherwise be obvious in the user interface. In general, you should try to use alerts sparingly to avoid annoying users.

Use action sheets to inform users of alternatives to an action. As Apple points out in their documentation, the Photos application contains a good example of using an action sheet (Figure 11-13). The user has a choice: use the photo as wallpaper, e-mail the photo, assign it to a contact, or do nothing (cancel). Action sheets are also appropriate when informing a user he or she is performing a potentially destructive task.

Image

Figure 11-13 An action sheet in the Photo application

UIAlertView and UIAlertViewDelegate

A UIAlertView presents a modal alert to users. The alert appears and floats above the underlying view. Displaying a UIAlertView is easy: Create the class and display it. As the alert is modal, a user can do nothing else until she clicks one of the alert’s buttons. The UIAlertViewDelegate handles the alert’s button actions. If you add buttons to an alert, implement the clickedButtonAtIndex: method in your delegate. Other methods you might implement include the alertViewCancel: or didPresentAlertView: method. For a complete listing, refer to Apple’s UIAlertViewDelegate Protocol Reference.

Alerts should not be misused. Present too many alerts, and you have an annoying application. Also, place buttons according to Apple’s recommendation. When alerting a user to a potentially destructive outcome, place the Cancel button on the right (Figure 11-14). When alerting a user to a harmless action, place the Cancel button on the left (Figure 11-15).

Image

Figure 11-14 Alert warning of a potentially destructive action

Image

Figure 11-15 Alert warning of a benign action

Try This
Creating a Simple UIAlertView

1. Create a new View-based Application. Name the application AlertsProgress.

2. Change AlertsProgressViewController to adopt the UIAlertViewDelegate protocol (Listing 11-7).

Listing 11-7 AlertsProgressViewController.h

#import <UIKit/UIKit.h>
@interface AlertsProgressViewController : UIViewController
<UIAlertViewDelegate> {
}
@end

3. Open AlertsProgressViewController.m and add an alert in the viewDidLoad method (Listing 11-8). Also, implement the didDismissWithButtonIndex: delegate method.

Listing 11-8 AlertsProgressViewController.m

#import "AlertsProgressViewController.h"
@implementation AlertsProgressViewController
-(void)viewDidLoad {
 [super viewDidLoad];
 UIAlertView * myAlert = [[[UIAlertView alloc]
    initWithTitle:@"View Loaded" message:@"View loaded successfully."
    delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease];
 [myAlert show];
}
-(void)alertView:(UIAlertView *)alertView
    didDismissWithButtonIndex: (NSInteger) buttonIndex {
 NSLog(@"buttonIndex: %i", buttonIndex);
}
-(void)dealloc {
 [super dealloc];
}
@end

4. Click Run to try the application in the iPhone Simulator (Figure 11-16). Tap the button, and the button’s index is logged to the Debugger Console.

In this simple application, the view’s controller implements a UIAlertViewDelegate. Upon clicking the button, the delegate’s didDismissWithButtonAtIndex method executes. This method determines which button a user clicked and routes the user accordingly. The didDismissWith ButtonAtIndex method is an instance method declared in UIAlertViewDelegateProtocol and has the following signature:

Image

Figure 11-16 A simple alert

-(void)alertView:(UIAlertView *) alertView didDismissWithButtonAtIndex:
(NSInteger) buttonIndex

The method takes the clicked button’s index as an NSInteger via the buttonIndex parameter. After processing, this method dismisses the alert.

You created a UIAlertView instance through the following code:

UIAlertView * myAlert = [[[UIAlertView alloc] initWithTitle:@"View
Loaded" message:@"View loaded successfully." delegate:self
cancelButtonTitle:nil otherButtonTitles:nil] autorelease];

This method is convenient for initializing the alert view. If you wish, you can add more buttons to an alert using the otherButtonTitles parameter in the initWithTitle method. The next task illustrates an alert with two buttons.

Try This
Using an Alert with Multiple Buttons

1. Open AlertsProgress in Xcode.

2. Modify AlertsProgressViewController’s viewDidLoad method so it matches Listing 11-9.

Listing 11-9 Code to display an alert with two buttons

-(void)viewDidLoad {
 [super viewDidLoad];
 UIAlertView * myAlert = [[[UIAlertView alloc] initWithTitle:
    @"View Loaded" message:@"View loaded successfully."
    delegate:self cancelButtonTitle:@"OK"
    otherButtonTitles:@"Cancel",nil] autorelease];
 [myAlert show];
}

3. Click Run. The alert shows two buttons (Figure 11-17). Click either button, and the button’s index is logged to the debugger console. You use this index to determine which button is clicked and route processing accordingly.

Image

Figure 11-17 An alert with two buttons

UIActionSheet and UIActionSheetDelegate

While an alert displays as a pop-up box, the UIActionSheet slides in from a view’s bottom, a view’s toolbar, or a view’s tab bar. You set where a toolbar slides in from when you display the action sheet. For instance, the following code slides the action sheet from the view’s bottom.

[myActionSheet showInView:self.view];

The action sheet’s bottom is aligned with the view’s bottom. Note that if you use this setting when using a tab bar or toolbar, the action sheet’s bottom is hidden by the bar. To prevent this, you display the action sheet using the showFromTabBar: or showFromToolBar: method. For instance, the following code slides the action sheet from the tab bar’s top and aligns the action sheet’s bottom with the bottom of the tab bar.

[myActionSheet showFromTabBar:self.view];

UIActionSheets are otherwise very similar to UIAlertViews. You specify an action sheet’s delegate by creating a class adopting the UIActionSheetDelegate. You use this delegate to implement button actions. Methods are similar to UIAlertViewDelegate’s. For instance, the following code handles a button click.

-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:
(NSInteger) buttonIndex

Try This
Using a UIActionSheet

1. Open AlertsProgress in Xcode.

2. Open AlertsProgressViewController.h and change the class so that it adopts the UIActionSheetDelegate protocol (Listing 11-10).

Listing 11-10 AlertsProgressViewController.h

#import <UIKit/UIKit.h>
@interface AlertsProgressViewController : UIViewController
<UIActionSheetDelegate> {
}
-(IBAction) removeAll: (id) sender;
@end

3. Add an IBAction called removeAll. In the method’s implementation, add a UIActionSheet that asks the user for confirmation (Listing 11-11). Remove the viewDidLoad method.

Listing 11-11 AlertsProgressViewController.m

#import "AlertsProgressViewController.h"
@implementation AlertsProgressViewController
-(IBAction) removeAll: (id) sender {
 UIActionSheet * myActionSheet = [[[UIActionSheet alloc]
       initWithTitle: @"Remove all?" delegate:self cancelButtonTitle:
       @"No" destructiveButtonTitle: @"Yes"
       otherButtonTitles:@"Not Sure",nil] autorelease];
 [myActionSheet showInView:self.view];
}
-(void) actionSheet: (UIActionSheet *) actionSheet
       didDismissWithButtonIndex: (NSInteger) buttonIndex {
 NSLog(@"buttons index: %i", buttonIndex);
 if(buttonIndex == [actionSheet cancelButtonIndex]) {
   NSLog(@"cancelled...");
 }
}
-(void)dealloc {
 [super dealloc];
}
@end

4. Open AlertsProgressViewController.xib, add a button to the view, and connect the removeAll action in the File’s Owner to the button’s Touch Up Inside event (Figure 11-18). Save your changes.

5. Open AlertsProgressViewController.m and implement the didDismissWithButtonIndex: method (Listing 11-11).

6. Click Run (Figure 11-19). Click each button in the action sheet, and the debugger console should produce logging results similar to Listing 11-12.

Listing 11-12 Debugger console output

Attaching to process 11451.
2010-09-08 10:06:34.529 AlertsProgress[11451:207] buttons index: 0
2010-09-08 10:06:37.186 AlertsProgress[11451:207] buttons index: 1
2010-09-08 10:06:39.511 AlertsProgress[11451:207] buttons index: 2
2010-09-08 10:06:39.512 AlertsProgress[11451:207] cancelled...

Image

Figure 11-18 Connecting File’s Owner removeAll action to a button

Image

Figure 11-19 A UIActionSheet in action

Image

Figure 11-20 An application badge tells me I have 39 e-mails in my inbox.

Application Badges

The iPhone’s Mail application illustrates using a badge. For instance, in Figure 11-20, I have 39 e-mails in my inbox. Using this functionality is easy. Simply access your application’s applicationBadgeNumber property and set it. A user’s iPhone will remember the value between uses of your program. To clear a badge, simply set its value to zero.

Try This
Adding an Application Badge

1. Open the previous task in Xcode.

2. Modify the didDismissWithButtonIndex method to match Listing 11-13.

Listing 11-13 The didDismissWithButtonIndex method modified to use an application badge

-(void) actionSheet: (UIActionSheet *) actionSheet
      didDismissWithButtonIndex: (NSInteger) buttonIndex {
 if(buttonIndex == [actionSheet cancelButtonIndex])
  [UIApplication sharedApplication].applicationIconBadgeNumber -= 1;
 else if (buttonIndex == [actionSheet destructiveButtonIndex])
  [UIApplication sharedApplication].applicationIconBadgeNumber += 1;
}

Image

Figure 11-21 The application has an application badge.

3. Click Build And Go to run the application.

4. Click the “Yes” button four or five times. Quit the application, but keep the simulator running. The application’s icon is adorned with an application badge (Figure 11-21).

5. Start the application again; click the “No” button a few times. Quit the application, but keep the simulator running and notice the application’s badge was decremented.

Summary

In this chapter you learned techniques for alerting users. Tasks that take time to complete should provide feedback to a user. When an application can estimate how long a task will take to complete, provide a UIProgressView. When an application cannot estimate how long a task will take to complete, provide a UIActivityView. When an unusual situation arises that requires a user decision, present a UIAlertView. When a user is making a decision that is potentially destructive, present a UIAlertView or a UIActionSheet, depending upon the uniqueness of the situation. If the situation is something commonly occurring while using an application, use a UIActionSheet. If it is an unusual situation, use a UIAlertView. But be careful not to overuse these two controls, as they interrupt an application’s flow.

This chapter ended by presenting application badges. Application badges are useful to alert a user of unprocessed items or items needing a user’s attention. Application badges are easy to incorporate into your application, but like alerts and action sheets, they should not be misused. For instance, you should not use an application badge to tell a user how many notes he or she has written in the Notes application. These notes do not require some action. Informing a user how many unread e-mails are in his or her inbox is an appropriate application badge use. Use your best judgment.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset