Hour 10. Getting the User’s Attention


What You’ll Learn in This Hour:

Image Different types of user notifications

Image How to create alert controllers

Image Methods for collecting input from alerts

Image How to use action sheet alert styles

Image How to implement short sounds and vibrations


iOS presents developers with many opportunities for creating unique user interfaces, but certain elements must be consistent across all applications. When users need to be notified of an application event or make a critical decision, it is important to present them with interface elements that immediately make sense. In this hour, we look at several different ways an application can notify a user that something has happened. It’s up to you to determine what that something is, but these are the tools you need to keep users of your apps “in the know.”

Alerting the User

Applications on iOS are user centered, which means they typically don’t perform utility functions in the background or operate without an interface. They enable users to work with data, play games, communicate, or carry out dozens of other activities. Despite the variation in activities, when an application needs to show a warning, provide feedback, or ask the user to make a decision, it does so in a common way. Cocoa Touch leverages a variety of objects and methods to gain your attention, including alert controllers and System Sound Services. Unlike many of the other objects we’ve worked with, these require us to build them in code. So don’t start digging through the Interface Builder (IB) Object Library just yet.


Note

Did you notice that I said applications typically don’t operate in the background? That’s because, with iOS 4 or later, some do. Applications running in the background have a unique set of capabilities, including additional types of alerts and notifications. You learn more about these in Hour 22, “Building Background-Ready Applications.”


Alert Controllers

Alert controllers (UIAlertController) are an addition to iOS 8 that unifies a few long-standing methods of getting user input. The UIAlertController is used for creating a view and view controller that can display either a simple alert, or a multiple-choice list. These alert styles are known as alerts and action sheets, respectively, and were previously handled by the UIAlertView and UIActionSheet classes.

The actions (buttons) that users can access within an alert controller are unique objects of the type UIAlertAction and are added to an alert controller using the addAction method. UITextField objects can also be added using the UIAlertController method addTextFieldWithConfigurationHandler.

Alerts

Sometimes users need to be informed of changes when an application is running. More than just a change in the current view is required when an internal error event occurs (such as low-memory condition or a dropped network connection), for example, or upon completion of a long-running activity. In these cases, you want to display what we’ll call an alert.

A UIAlertController with the Alert style is used to present a translucent floating modal window with a message, a few option buttons, and text entry fields, as shown in Figure 10.1.

Image

FIGURE 10.1 An alert example, generated in Safari.

Implementing an alert takes little effort: You declare a UIAlertController object, initialize it, and present it. Consider the code fragment in Listing 10.1.

LISTING 10.1 Implementing an Alert-styled UIAlertController


 1:     let alertController = UIAlertController(title: "Email Address",
 2:         message: "Please enter your email address:",
 3:         preferredStyle: UIAlertControllerStyle.Alert)
 4:
 5:     let defaultAction = UIAlertAction(title: "Ok",
 6:         style: UIAlertActionStyle.Default,
 7:         handler: nil)
 8:
 9:     let destructiveAction = UIAlertAction(title: "Erase My Device",
10:         style: UIAlertActionStyle.Destructive,
11:         handler: nil)
12:
13:alertController.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
14:         textField.placeholder="Email Address"
15:         textField.keyboardType=UIKeyboardType.EmailAddress
16:     }
17:
18:     alertController.addAction(defaultAction)
19:     alertController.addAction(destructiveAction)
20:
21:     presentViewController(alertController, animated: true, completion: nil)


In lines 1–3, I declare a constant alertController to hold an instance of UIAlertController. The initialization method of the alert view provides three parameters we can customize:

Image title: The title that is displayed at the top of the alert

Image message: Sets the string that will appear in the content area of the dialog box

Image preferredStyle: Defines whether the alert is a dialog box (UIAlertControllerStyle.Alert) or an action sheet in appearance (UIAlertControllerStyle.ActionSheet)

Lines 5–7 configure the first of two buttons that will be added to the alert. A constant, defaultAction, of the type UIAlertAction is created using an alert action convenience initialization method. This method uses a title parameter to set the title on the button, a handler (code to be executed when the button is touched—we’ll get to that in a minute or two), and a style, which can be one of three values:

Image UIAlertActionStyle.Default: The default button style—any general “actions” should use this style. You might see this used for an Ok button.

Image UIAlertActionStyle.Cancel: An action that will cancel the current operation (usually labeled Cancel).

Image UIAlertActionStyle.Destructive: This button uses red text in its label and is displayed when there is the potential to lose data by completing the action (such as erasing a form, for example).

This first button displays the label Ok, does nothing when it is touched (handler: nil), and uses the default alert action style.

Lines 9–11 configure a second button, destroyAction, using the destructive style, another nil handler, and the label Erase My Device.

Lines 13–16 add a text field to the controller with the addTextFieldWithConfigurationHandler. This method accepts a closure as its parameter, which, in turn, accepts a single textField variable. The textfield is automatically initialized for us, we just need to adjust its attributes as we see fit (lines 14–15). Any UITextField variable properties can be set in this closure.

Lines 18–19 use the UIViewController method addAction to add the defaultAction and destroyAction buttons to the alert controller.

Finally, in line 21, the alertController object that we’ve lovingly crafted is displayed to the user using the UIViewController method presentViewController.

So, what exactly does this alert do if we were to try to use it? Absolutely nothing. It will display, show the text field and buttons that we configured, and then just go away as soon as we touch a button. That’s exactly what we get if we set the handler parameter to nil when defining the alert’s actions.

Assuming the goal is to actually react to the alert, we’ll need to do a teensy bit more work.

Responding to an Action

When I constructed the alert example in Listing 10.1, I defined two actions, both with a handler of nil. I also took the time to write a sidebar explaining (well, trying to explain) how handlers are often defined as blocks of code called closures. Putting two and two together, how do you think we’ll be able to react to the user touching one of the buttons? That’s right, you’ve got it: by defining a handler closure for each action! (That was your guess, yes?)

By using closures with actions, we can define what happens when the user touches any of the UIAlertAction buttons that are added to a UIAlertController. Each action is independent, so we don’t have to worry about figuring out which button was pressed and then write a big method to handle everything.

If the alert view is displaying text fields, we can access the fields through the alert controller’s textFields variable property (an array of AnyObject), where textFields[0] is the first text field added to the controller, textFields[1] is the second, and so on.

For example, if I were to revise the defaultAction that I created in lines 5–7 of Listing 10.1 so that it would retrieve the contents of the alert’s Email Address field, the code would look at bit like the fragment in Listing 10.2.

LISTING 10.2 Defining Handlers Within Alert Actions


1: let defaultAction = UIAlertAction(title: "Ok",
2:     style: UIAlertActionStyle.Default,
3:     handler: {(alertAction: UIAlertAction!) in
4:     // The user touched Ok!
5:   let emailAddress: String = (alertController.textFields![0] as UITextField).text
6: })


Line 3 starts the handler and its closure. This is the code that will be executed when the user invokes the action by touching the Ok button. Line 5 sets a constant emailAddress to the text variable property of the first text field within the alertController’s textFields array. Note that we have to both unwrap textFields before we can access it, and we have to cast the member of the array as a UITextField because (for reasons I can’t fathom) textFields is defined by Cocoa as an array of AnyObject rather than an array of UITextFields.

While the syntax may look ugly, it should be pretty clear what it does. (If it isn’t, don’t worry; we’ll try it out ourselves in a few moments.)

Action Sheets

Alerts are used to display messages that indicate a change in state or a condition within an application that a user should acknowledge, but are other instances where a different solution would be better. For example, if an application provides the option to share information with a friend, the user might be prompted for the method of sharing (such as sending an email, uploading a file, and so on). In this case, the iOS-way of alerting the user to making this decision is by way of an action sheet.

You can see this behavior when touching and holding a link in Safari, as shown in Figure 10.2.

Image

FIGURE 10.2 Action sheets ask you to choose between several options.

In previous editions of this book, I stated that implementing action sheets were very similar to alerts. With iOS 8, that statement is now false. In fact, implementing action sheets is now identical to alerts—with the exception of not being able to use text fields within an action sheet.

For example, take a look at Listing 10.3.

LISTING 10.3 Implementing a UIActionSheet Class


 1:  let alertController = UIAlertController(title: "Do Something",
 2:      message: "Please choose an action:",
 3:      preferredStyle: UIAlertControllerStyle.ActionSheet)
 4:
 5:  let defaultAction = UIAlertAction(title: "Send Memo",
 6:      style: UIAlertActionStyle.Default,
 7:      handler: {(alertAction: UIAlertAction!) in
 8:      // The user touched Send Memo
 9:  })
10:
11:  let destructiveAction = UIAlertAction(title: "Erase My Device",
12:      style: UIAlertActionStyle.Destructive,
13:      handler: {(alertAction: UIAlertAction!) in
14:      // The user wants us to erase the device. Oh well!
15:  })
16:
17:  let cancelAction = UIAlertAction(title: "Cancel",
18:      style: UIAlertActionStyle.Cancel,
19:      handler: nil)
20:
21:  alertController.addAction(defaultAction)
22:  alertController.addAction(destructiveAction)
23:  alertController.addAction(cancelAction)
24:
25:  presentViewController(alertController, animated: true, completion: nil)


Lines 1–3 declare and instantiate an instance of UIAlertController called alertContoller. The setup of the controller is the same as with an alert, but we set the preferredStyle to UIAlertControllerStyle.ActionSheet.

Lines 5–9, 10–15, and 17–19 define three actions (buttons) that will be displayed by the controller. Remember, the handler for each action is where we can add code to react when the user touches the button. If the handler is set to nil (as in the case of the cancel action), touching the button simply dismisses the action sheet.

Lines 21–23 add the three actions to the alert controller, and line 25 presents the controller onscreen.


Tip

Action sheet and alert styles are identical in how they are initialized, modified, and ultimately, acted upon. On some devices, however, an action sheet can be associated with an onscreen control—like a toolbar icon. This changes the onscreen appearance slightly, but doesn’t affect how you create and populate the alert controller.



Try it Yourself: Frustration in the Playground

The Playground is a great place to test things, but Apple, it seems, is still installing the Playground equipment. It would be great to test alerts in the playground, but in the current state, it only barely works. The problem with the Playground is that you don’t have a view controller to play with. If you want one, you’ve got to programmatically create it—and by the time you’ve done that, you’ve pretty much written the application.

The Playground includes a module that gives us a kinda-sorta view controller that can display system-generated views like the alert view.

For example, create a new iOS Playground, then input the following code. This is exactly the same as the previous listing, with the addition of two new lines (bolded):

import UIKit
import XCPlayground

let alertController = UIAlertController(title: "Do Something",
    message: "Please choose an action:",
    preferredStyle: UIAlertControllerStyle.ActionSheet)

let defaultAction = UIAlertAction(title: "Send Memo",
    style: UIAlertActionStyle.Default,
    handler: {(alertAction: UIAlertAction!) in
        // The user touched Send Memo
})

let destructiveAction = UIAlertAction(title: "Erase My Device",
    style: UIAlertActionStyle.Destructive,
    handler: {(alertAction: UIAlertAction!) in
        // The user wants us to erase the device. Oh well!
})

let cancelAction = UIAlertAction(title: "Cancel",
    style: UIAlertActionStyle.Cancel,
    handler: nil)

alertController.addAction(defaultAction)
alertController.addAction(destructiveAction)
alertController.addAction(cancelAction)

XCPShowView("Alert", alertController.view)

The import XCPlayground line adds the module necessary to deal with dynamic views, like an alert. The last line’s method XCPShowView acts as our view controller. We use it to load the view variable property from the alertController and display it. It isn’t displayed in a little popup window, however, it is shown in the assistant editor. Position your cursor over one of the output lines on the right and click the circle icon, or choose View, Show Toolbar to show the toolbar and access the assistant editor.

After a few seconds, you should see a somewhat cruddy visualization of the alert.

We can make things slightly better by running the Playground code in the iOS Simulator instead of the Playground window. To do this, open the File Inspector (View, Utilities, Show File Inspector; Option-Command-1), and then click Run in Full Simulator within the Playground settings section.

Finally, choose Editor, Execute Playground from the menu bar. The standard iOS Simulator will start and show a prettier (although flickering and eventually crashing) version of the alert view.

I fully expect the Playground will eventually handle things like this with ease, and it is certainly possible that I haven’t yet found the magic recipe for displaying the alert view yet. Play around and see what you can make the Playground do. However, give it a bit of slack; the Playground equipment needs a bit more breaking in before we spend a lot of time there.


System Sound Services

Visual notifications are great for providing feedback to a user and getting critical input. Other senses, however, can prove just as useful for getting a user’s attention. Sound, for example, plays an important role on nearly every computer system (regardless of platform or purpose). Sounds tell us when an error has occurred or an action has been completed. Sounds free a user’s visual focus and still provide feedback about what an application is doing.

Vibrations take alerts one step further. When a device has the ability to vibrate, it can communicate with users even if they can’t see or hear it. For the iPhone, vibration means that an app can notify users of events even when stowed in a pocket or resting on a nearby table. The best news of all? Both sounds and vibrations are handled through the same simple code, meaning that you can implement them relatively easily within your applications.

To enable sound playback and vibration, we take advantage of System Sound Services. System Sound Services provides an interface for playing back sounds that are 30 seconds or less in length. It supports a limited number of file formats (specifically CAF, AIF, and WAV files using PCM or IMA/ADPCM data). The functions provide no manipulation of the sound, nor control of the volume, so you do not want to use System Sound Services to create the soundtrack for your latest and greatest iOS game. In Hour 19, “Working with Rich Media,” we explore additional media playback features of iOS.

iOS supports three different notifications using this API:

Image Sound: A simple sound file is played back immediately. If the device is muted, the user hears nothing.

Image Alert: Again, a sound file is played, but if the device is muted and set to vibrate, the user is alerted through vibration.

Image Vibrate: The device is vibrated, regardless of any other settings.

Accessing Sound Services

To use System Sound Services from a project, you must add the AudioToolbox framework and any sound files you want to play. Because AudioToolbox is an Apple-provided framework and supports being imported as a module, we can prepare an application to use it by adding a single line to our code:

import AudioToolbox

Unlike most of the other development functionality discussed in this book, the System Sound Services functionality is not implemented as a class. Instead, you use more traditional C-style function calls to trigger playback.

To play audio, the two functions you use are AudioServicesCreateSystemSoundID and AudioServicesPlaySystemSound. You also need to declare a variable of the type SystemSoundID. This represents the sound file that we are working with. To get an idea of how it all comes together, look at Listing 10.4.

LISTING 10.4 Loading and Playing a Sound


1:  var soundID: SystemSoundID = 0
2:  let soundFile: String = NSBundle.mainBundle().pathForResource("mysound", ofType: "wav")!
3:  let soundURL: NSURL = NSURL(fileURLWithPath: soundFile)!
4:  AudioServicesCreateSystemSoundID(soundURL, &soundID)
5:  AudioServicesPlaySystemSound(soundID)


This might seem a bit alien after all the objects we’ve been using. Let’s take a look at the functional pieces.

Line 1 starts things off by declaring a variable, soundID, that will be an iOS-generated integer that references the sound file we want to play. Next, in line 2, we declare and assign a string (soundFile) to the path of the sound file mysound.wav. This works by first using the NSBundle class method mainBundle to return an NSBundle object that corresponds to the directory containing the current application’s executable binary. The NSBundle object’s pathForResource:ofType method is then used to identify the specific sound file by name and extension.

Once we have the path as a String, we create an NSURL object from it in line 3. This is required for the AudioServicesCreateSystemSoundID function in line 4, which takes the location of the sound file as an NSURL object, along with a pointer to the soundID variable. The & denotes that we’re sending a “pointer” to soundID rather than the value of soundID. The AudioServicesCreateSystemSoundID function’s purpose is to update soundID with whatever magical integer references our sound on iOS.

After soundID has been properly set up, all that remains is playing it. Passing the soundID variable to the AudioServicesPlaySystemSound function, as shown in line 5, makes it happen.

Alert Sounds and Vibrations

The difference between an alert sound and a system sound is that an alert sound, if muted, automatically triggers a phone vibration. The setup and use of an alert sound is identical to a system sound. In fact, playing an alert is just a matter of substituting the function AudioServicesPlayAlertSound in place of AudioServicesPlaySystemSound.

Vibrations alone are even easier. To vibrate a compatible device (currently iPhones), you just provide the 32bit integer version of kSystemSoundID_Vibrate to AudioServicesPlaySystemSound:

AudioServicesPlaySystemSound(UInt32(kSystemSoundID_Vibrate))


Tip

The need to use UInt32() to convert the kSystemSoundID_Vibrate constant into something that AudioServicesPlaySystemSound can work with seems to be an oversight on Apple’s part. I hope that in the future we can just use kSystemSoundID_Vibrate directly.


Now that you understand the different alert styles we have to work with, it’s time to implement them for real. We’ll test several variations of alerts, action sheets, and sounds in this hour’s tutorial.


Tip

Attempting to vibrate a device without vibration capabilities (like an iPad) will fail silently. You can safely leave vibration code in your app regardless of the device you are targeting.


Exploring User Alert Methods

Because all the logic for implementing alerts, action sheets, or System Sound Services is contained in small, easy-to-understand chunks of code, this hour’s project differs a bit from what you’ve seen in other hours. We treat this hour’s project like a sandbox. We set it up, and then spend a good amount of time talking about the code that needs to be written to make it work, and we test it along the way.

You’ll generate alerts, alerts with multiple buttons, alerts with fields, action sheet alerts, sounds, and even vibrate your device (if it’s an iPhone, that is).

Implementation Overview

Unlike other projects where our UI design was intimately tied to the code, this tutorial’s interface is rather inconsequential. We’re simply interested in creating buttons to trigger actions that demonstrate the different alert methods and providing a single output area so we can see how the user responded. Everything to generate alerts, action sheets, sounds, and vibrations is handled entirely in code, so the sooner we get the framework set up for the project, the sooner we can get to the implementation logic.

Setting Up the Project

To practice using these alert classes and methods, we need to create a new project with buttons for activating the different styles of notifications. Open Xcode and create a new project based on the Single View Application template. Name the project GettingAttention.

Several resources that we need in this project aren’t there by default, notably the sounds that we will be playing with System Sound Services. Let’s add these important resources now.

Adding the Sound Resources

With your project open in Xcode, return to the Finder and navigate to the Sounds folder within this hour’s project folder. Drag the folder into your Xcode project folder, choosing to copy the files and create groups when prompted.

You should now see the files listed in your project group, as shown in Figure 10.3.

Image

FIGURE 10.3 Add the sound files to your project.

Planning the Variables and Connections

The last step before we can create our GettingAttention application is to figure out what outlets and actions we need to fully test everything that we want. As mentioned earlier, this is a barebones app, nothing flashy. The only outlet we need is for a single label (UILabel) that provides some feedback to what the user has done. This will be named userOutput.

In addition to the outlet, we need a total of seven different actions, all to be triggered by different buttons in the user interface: doAlert, doMultiButtonAlert, doAlertInput, doActionSheet, doSound, doAlertSound, and finally, doVibration.

That’ll do it. Everything else is handled in code. Let’s create the interface and make our connections.

Designing the Interface

Open the Main.storyboard file in Interface Builder (IB), select the view controller, then use the Attributes Inspector to set the simulated size to the device that you’re using. This is, of course, optional, but it helps keep us focused on creating a quick and dirty UI rather than elegant design.

We need to add seven buttons and a text label to the empty view. You should be getting quite familiar with this process by now.

Add a button to the view by opening the Object Library (View Utilities, Show Object Library) and dragging a button (IUButton) to the View window. Add six more buttons using the library or by copying and pasting the first button.

Change the button labels to correspond to the different notification types that we’ll be using. Specifically, name the buttons (top to bottom) as follows:

Image Alert Me!

Image Alert with Buttons!

Image I Need Input!

Image Lights, Camera, Action Sheet

Image Play Sound

Image Play Alert Sound

Image Vibrate Device

Drag a label (UILabel) from the library to the bottom of the view. Remove the default label text and set the text to align center. The interface should resemble Figure 10.4. I’ve chosen to cluster my buttons based on their function. You can arrange yours however you want.

Image

FIGURE 10.4 Create an interface with seven buttons and a label at the bottom.

Creating and Connecting the Outlets and Actions

The interface itself is finished, but we still need to make the connection between the objects and our code. It’s probably self-explanatory, but the connections you will be building are listed here.

First the outlet:

Image User Output Label (UILabel): userOutput

And then the actions:

Image Alert Me! (UIButton): doAlert

Image Alert with Buttons! (UIButton): doMultiButtonAlert

Image I Need Input! (UIButton): doAlertInput

Image Lights, Camera, Action Sheet (UIButton): doActionSheet

Image Play Sound (UIButton): doSound

Image Play Alert Sound (UIButton): doAlertSound

Image Vibrate Device (UIButton): doVibration

With the Main.storyboard file selected, click the assistant editor button, and then hide the project navigator and document outline (Editor, Hide Document Outline) to make room for your connections. The ViewController.swift file should be visible to the right of your interface.

Adding the Outlet

Control-drag from our single lonely label to just below the class line in ViewController.swift. When prompted, choose to create a new outlet named userOutput, as shown in Figure 10.5.

Image

FIGURE 10.5 Connect the label to userOutput.

Adding the Actions

Now, Control-drag from the Alert Me! button to just below the @IBOutlet declaration in the ViewController.swift file, connecting to a new action named doAlert, as shown in Figure 10.6.

Image

FIGURE 10.6 Connect each of the buttons to its corresponding action.

Repeat this process for the other six buttons. Alert with Buttons! connects to doMultiButtonAlert; I Need Input! should connect to the doAlertInput method; Lights, Camera, Action Sheet to doActionSheet; Play Sound to doSound; Play Alert Sound to doAlertSound; and Vibrate Device to doVibration.

The framework for our test of notifications is finished, and we’re ready to jump into code. Switch back to the standard editor and display the project navigator (Command-1). Open the ViewController.swift file; we start by implementing a simple alert-style dialog box

Implementing Alerts

The simplest case of an alert that a user can encounter (and that a developer can develop) is an alert that is displayed and dismissed without changing the flow of the application at all. In other words, the alert is simply that: an alert. When the user presses the button to dismiss it, nothing else changes.

Edit ViewController.swift and enter the code shown in Listing 10.5 for the doAlert implementation.

LISTING 10.5 Implementing the doAlert Method


 1: @IBAction func doAlert(sender: AnyObject) {
 2:     let alertController = UIAlertController(title: "Alert Me Button Selected",
 3:         message: "I need your attention NOW!",
 4:         preferredStyle: UIAlertControllerStyle.Alert)
 5:
 6:     let defaultAction = UIAlertAction(title: "Ok",
 7:         style: UIAlertActionStyle.Cancel,
 8:         handler: nil)
 9:
10:     alertController.addAction(defaultAction)
11:     presentViewController(alertController, animated: true, completion: nil)
12: }


If you were paying attention at the start of this hour’s lesson, this method should look familiar.

In lines 2–4, we declare and configure our instance of UIAlertController in a variable called alertController. The alert is initialized with a title (Alert Me Button Selected), a message (I need your attention NOW!), and a preferred style of Alert.

There is a single action, defaultAction, defined in lines 6–8. This creates a button with the title Ok, styled with UIAlertActionStyle.Cancel and a nil handler. Why the Cancel style? Because the purpose of the button is to just get rid of the alert; there is no handler to react to the action. You could also use the default style, but “cancel” just makes more sense to me.

Line 10 adds defaultAction to alertController, and line 11 displays it onscreen.

You can now run the project and test the first button, Alert Me!, Figure 10.7 shows the outcome of your first alert implementation.

Image

FIGURE 10.7 In its simplest form, an alert view displays a message and button to dismiss it.


Tip

An alert doesn’t have to be a single-use object. If you’re going to be using an alert repeatedly, create a variable property for the alert controller, set it up when your view is loaded, and show it as needed.


Creating Multibutton Alerts

An alert with a single button is easy to implement because you do not have to program any additional logic. The user taps the button, the alert is dismissed, and execution continues as normal. In most cases, however, your application will display multiple button choices and react appropriately to whatever option the user chooses.

How difficult is this? Not at all. Each action that is added to an alert can include a handler closure to carry out specific tasks. Create actions, set handlers, add the actions to the alert controller—all done. To test this, write an updated multibutton version of the doAlert method within the doMultiButtonAlert method stub created earlier.

Listing 10.6 shows my implementation.


Tip

At most, an alert view can display five buttons (including the button designated as the cancel button) simultaneously. Attempting to add more may result in some unusual onscreen effects, such as display of clipped/partial buttons.


LISTING 10.6 Implementing the doMultipleButtonAlert Method


 1: @IBAction func doMultiButtonAlert(sender: AnyObject) {
 2:     let alertController = UIAlertController(title: "Alert with Buttons Selected",
 3:         message: "Options are good for people!",
 4:         preferredStyle: UIAlertControllerStyle.Alert)
 5:
 6:     let nowAction = UIAlertAction(title: "Do Something Now",
 7:         style: UIAlertActionStyle.Default,
 8:         handler: {(alertAction: UIAlertAction!) in
 9:             self.userOutput.text="Pressed Now"
10:     })
11:
12:     let laterAction = UIAlertAction(title: "Do Something Later",
13:         style: UIAlertActionStyle.Default,
14:         handler: {(alertAction: UIAlertAction!) in
15:             self.userOutput.text="Pressed Later"
16:     })
17:
18:     let cancelAction = UIAlertAction(title: "Never Do It",
19:         style: UIAlertActionStyle.Cancel,
20:         handler: {(alertAction: UIAlertAction!) in
21:             self.userOutput.text="Pressed Never"
22:     })
23:
24:     alertController.addAction(nowAction)
25:     alertController.addAction(laterAction)
26:     alertController.addAction(cancelAction)
27:
28:     presentViewController(alertController, animated: true, completion: nil)
29: }


In this new implementation, three actions (buttons) are configured in lines 6–10, 12–16, and 18–22. Unlike the previous method, these actions all include a handler closure that sets the userOutput label to a message corresponding to the button pressed.

Notice that in each closure (lines 9, 15, and 21), I prefix the userOutput object with self. Closures act like independent isolated pieces of code and can’t access variables and methods defined within your class without specifying self. If you happen to leave this off, Xcode will warn you of the error and offer to fix it for you.

Lines 24–26 add the actions to the alert controller, while line 28 displays it.

Give the application another test run. Pressing Alert with Buttons! should now open the alert view displayed in Figure 10.8.

Image

FIGURE 10.8 The alert view now includes a total of three buttons.

Try touching one of the alert buttons. The alert view is dismissed, but, more important, the text in the userOutput label is set to a message identifying the button.


Caution: Always Active

Don’t assume that application processing stops when the alert window is on the screen. Your code continues to execute after you show the alert.


Using Fields in Alerts

Although buttons can be used to generate user input from an alert, you might have noticed that some applications actually present text fields within an alert box. The App Store, for example, prompts for your Apple ID password before it starts downloading a new app.

To add fields to your alert dialogs, you use the alert controller’s addTextFieldWithConfigurationHandler. The text field is added and can be configured using any of the variable properties supported by the UITextField class.

As an example, let’s create an email entry alert that collects an email address, then displays it when the alert’s button is touched. Update the doAlertInput method stub with the implementation shown in Listing 10.7.

LISTING 10.7 The doAlertInput Implementation


 1: @IBAction func doAlertInput(sender: AnyObject) {
 2:     let alertController = UIAlertController(title: "Email Address",
 3:         message: "Please enter your email address below:",
 4:         preferredStyle: UIAlertControllerStyle.Alert)
 5:
 6:     alertController.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
 7:         textField.placeholder="Email Address"
 8:         textField.keyboardType=UIKeyboardType.EmailAddress
 9:     })
10:
11:     let defaultAction = UIAlertAction(title: "Ok",
12:         style: UIAlertActionStyle.Default,
13:         handler: {(alertAction: UIAlertAction!) in
14:           let emailAddress: String = (alertController.textFields![0] as UITextField).text
15:           self.userOutput.text="Entered '(emailAddress)'"
16:     })
17:
18:     alertController.addAction(defaultAction)
19:     presentViewController(alertController, animated: true, completion: nil)
20: }


The text field is added in lines 6–9. Note that the UITextField variable properties placeholder and keyboardType are used to configure the appearance of the field. You might also consider using the secureTextEntry Boolean variable to make the field appear as a password entry.

Lines 11–16 create an action, defaultAction, with a handler that updates the userOutput text to show the email address the user entered. As described earlier this hour, the text field is accessed from the alertController object’s textFields array. This array is created for us automatically each time we add a text field to an alert.

Line 18 adds the defaultAction to the alert controller. Line 19 displays the alert controller.

Run the application and touch the I Need Input! button. You should see the alert, as demonstrated in Figure 10.9. Enter an email address and touch Ok; the address is retrieved from the alert and displayed in the output label.

Image

FIGURE 10.9 The alert view now displays a plain-text entry field.

Implementing Action Sheets

Now that you’ve implemented several types of alerts, action sheets will pose no difficulty at all. The setup and handling of an action sheet is more straightforward than an alert view because action sheets can do one thing and only one thing: show a list of actions (buttons).


Tip

Action sheets can take up to seven buttons on a 3.5-inch iPhone (including “cancel” and the “destructive” button) while maintaining a standard layout. If you exceed seven, however, the display automatically changes into a scrolling list. This gives you room to add as many options as you need.


To create our action sheet, we’ll implement the method stub doActionSheet created within the ViewController.swift file. Recall that this method is triggered by pushing the Lights, Camera, Action Sheet button. It displays the title Available Actions and has a cancel button named Cancel, a destructive button named Destroy, and two other buttons named Negotiate and Compromise.

Add the code in Listing 10.8 to the doActionSheet method.

LISTING 10.8 Implementing the doActionSheet Method


 1: @IBAction func doActionSheet(sender: AnyObject) {
 2:     let alertController = UIAlertController(title: "Available Actions",
 3:         message: "Choose something from this list",
 4:         preferredStyle: UIAlertControllerStyle.ActionSheet)
 5:
 6:     let negotiateAction = UIAlertAction(title: "Negotiate",
 7:         style: UIAlertActionStyle.Default,
 8:         handler: {(alertAction: UIAlertAction!) in
 9:             self.userOutput.text="Pressed Negotiate"
10:     })
11:
12:     let compromiseAction = UIAlertAction(title: "Compromise",
13:         style: UIAlertActionStyle.Default,
14:         handler: {(alertAction: UIAlertAction!) in
15:             self.userOutput.text="Pressed Compromise"
16:     })
17:
18:     let destroyAction = UIAlertAction(title: "Destroy",
19:         style: UIAlertActionStyle.Destructive,
20:         handler: {(alertAction: UIAlertAction!) in
21:             self.userOutput.text="Pressed Destroy"
22:     })
23:
24:     let cancelAction = UIAlertAction(title: "Cancel",
25:         style: UIAlertActionStyle.Cancel,
26:         handler: {(alertAction: UIAlertAction!) in
27:             self.userOutput.text="Pressed Cancel"
28:     })
29:
30:     alertController.addAction(negotiateAction)
31:     alertController.addAction(compromiseAction)
32:     alertController.addAction(destroyAction)
33:     alertController.addAction(cancelAction)
34:
35:
36:     if (alertController.popoverPresentationController != nil) {
37:         alertController.popoverPresentationController!.sourceView =
38:             sender as UIButton
39:         alertController.popoverPresentationController!.sourceRect =
40:             (sender as UIButton).bounds
41:     }
42:
43:     presentViewController(alertController, animated: true, completion: nil)
44: }


Lines 2–4 instantiate a new instance of UIAlertController—the same are our other methods—but this time, using the preferred style of UIAlertControllerStyle.ActionSheet.

The rest of the code should look exactly like what you’ve already seen this hour—with one major exception (lines 36–41). To understand why these lines are necessary, we have to know a bit about the action sheets and the iPad.

On the iPad, action sheets should not be displayed directly on top of a view. The Apple UI guidelines say that they must be displayed within a popover. A popover is a unique UI element that appears when an onscreen item is touched and usually disappears when you touch somewhere on the background. Popovers also incorporate a small arrow that points toward the UI element that invoked them. You learn more about popovers in the next hour’s lesson.

When we create an action sheet on an iPad, iOS automatically configures a popover controller for us and stores it in the alert controller’s variable property popoverPresentationController. In order for the popover controller to work, however, we need to set two variable properties of the popover controller: sourceView (the view that the popover is originating from) and sourceRect (a rectangle defining the area that the popover should point to). Aren’t these the same thing? Yep. Apple’s documentation even states we need one or the other. But unless both are set, the popover doesn’t work. Lines 37–38 set the sourceView to the button that was touched, whereas lines 39–40 set the sourceRect to the bounds of the same button.

On devices that aren’t an iPad, the popoverPresentationController is set to nil, so these configuration lines aren’t executed. The result? When running on the iPad, the action sheet appears in a popover. When running on an iPhone, the action sheet is shown in exactly the same manner we’d expect.

Run the application and touch the Lights, Camera, Action Sheet button to see the results. Figure 10.10 demonstrates the display.

Image

FIGURE 10.10 Action sheets can include cancel and destructive buttons, as well as buttons for other options.


Note: In a Popover? No Canceling!

When iOS is displaying an action sheet within a popover, it automatically removes the Cancel-styled action from the sheet. On devices that support popovers, the interface convention for “canceling” a popover is to touch outside of the popover display—in other words, no separate action is needed.


Implementing Alert Sounds and Vibrations

Recall that to use System Sound Services from a project, you need the AudioToolbox framework and any sound files you want to play. Because we already included the sound resources, we just need to add the framework. To import the AudioToolbox framework and make our code aware of its existence, add an import line to ViewController.swift. Insert this line immediately following the existing import directive:

import AudioToolbox

We’re now ready to play sounds and vibrate the device. Very little changes from the example code that we covered earlier this hour.

Playing System Sounds

The first thing that we want to implement is the doSound method for playing system sounds. These are short sounds that, when muted, will not result in an accompanying vibration. The Sounds folder that you added to the project during the setup contains a file soundeffect.wav that we will use to implement system sound playback.

Edit the ViewController.swift implementation file and complete doSound, as shown in Listing 10.9.

LISTING 10.9 Implementing the doSound Method


1: @IBAction func doSound(sender: AnyObject) {
2:     var soundID: SystemSoundID = 0
3:     let soundFile: String = NSBundle.mainBundle().pathForResource("soundeffect",
4:         ofType: "wav")!
5:     let soundURL: NSURL = NSURL(fileURLWithPath: soundFile)!
6:     AudioServicesCreateSystemSoundID(soundURL, &soundID)
7:     AudioServicesPlaySystemSound(soundID)
8: }


Line 2 declares soundID, a variable that refers to the sound file.

In lines 3–4, we declare and assign a string (soundFile) to the path of the sound file soundeffect.wav. Line 5 turns that string into an NSURL named soundURL.

In line 6, we use the AudioServicesCreateSystemSoundID function to create a SystemSoundID that represents the sound file.

Line 7 uses the AudioServicesPlaySystemSound function to play the sound.

Run and test the application. Pressing the Play Sound button should now play back the sound effect WAV file.


Caution: This Simulation May Be Broken!

Sometime during the development of Xcode 6.x, the ability for the simulator to play sounds via System Sound Services stopped working. By the time you read this, it might work again. Worst case, the code executes, but does nothing in the simulator. Try running it on a device and you should be fine.


Playing Alert Sounds with Vibrations

As mentioned earlier this hour, the difference between an alert sound and a system sound is that an alert sound, if muted, automatically triggers a vibration. The setup and use of an alert sound is identical to a system sound. In fact, to implement the doAlertSound method stub in GettingAttentionViewController.swift, use the same code as the doSound method in Listing 10.13, substituting the sound file alertsound.wav and using the function AudioServicesPlayAlertSound rather than AudioServicesPlaySystemSound:

AudioServicesPlayAlertSound(soundID)

After implementing the new method, run and test the application. Pressing the Play Alert Sound button plays the sound, and muting an iPhone causes the device to vibrate when the button is pressed.

Vibrating the Device

For our grand finale, we implement the final method in our GettingAttention application: doVibration. As you’ve already learned, the same System Sound Services that enabled us to play sounds and alert sounds also create vibrations. The magic we need here is the kSystemSoundID_Vibrate constant. When this value is substituted for the SystemSoundID and AudioServicesPlaySystemSound is called, the device vibrates. It’s as simple as that! Implement the doVibration method, as shown in Listing 10.10.

LISTING 10.10 Implementing the doVibration Method


@IBAction func doVibration(sender: AnyObject) {
    AudioServicesPlaySystemSound(UInt32(kSystemSoundID_Vibrate))
}


That’s all there is to it. You’ve now explored seven different ways of getting a user’s attention. These are techniques that you can use in any application to make sure that your user is alerted to changes that may require interaction and can respond if needed.

Further Exploration

Your next step in making use of the notification methods discussed in this hour is to use them. These simple, but important, UI elements will help facilitate many of your critical user interactions. One topic that is beyond the scope of this book is the ability for a developer to push notifications to an iDevice.

Even without push notifications, you might want to add numeric badges to your applications. These badges are visible when the application isn’t running and can display any integer you want—most often, a count of items identified as “new” within the application (such as new news items, messages, events, and so on). To create application badges, look at the UIApplication variable property applicationIconBadgeNumber. Setting this to anything other than 0 will create and display the badge.

Another area that you might like to explore is how to work with rich media (Hour 19). The audio playback functions discussed in this hour are intended for alert-type sounds only. If you’re looking for more complete multimedia features, you need to tap into the AVFoundation framework, which gives you complete control over recording and playback features of iOS.

Finally, this hour covered notifications that occur when your application is running. For information about generating notifications when your app is stopped, check out Hour 22.

Summary

In this hour, you learned about two types of modal dialogs that can be used to communicate information to an application user and to enable the user to provide input at critical points in time. Alerts and action sheets have different appearances and uses but very similar implementations. Unlike many of the UI components we’ve used in this book, you cannot instantiate these with a simple drag and drop in IB.

We also explored two nonvisual means of communicating with a user: sounds and vibrations. Using the System Sound Services (by way of the AudioToolbox framework), you can easily add short sound effects and vibrate your iDevice. Again, you have to implement these in code, but in fewer than five lines, you can have your applications making noises and buzzing in your users’ hands.

Q&A

Q. Can sounds be used in conjunction with alert views?

A. Yes. Because alerts are often displayed without warning, there is no guarantee that the user is looking at the screen. Using an alert sound provides the best chance for getting the user’s attention, either through an audible noise or an automatic vibration if the user’s sound is muted.

Q. Why don’t popovers work on the iPhone?

A. As of iOS 8, they do! That said, alert sheets, however, are still expected to be displayed in a certain way on both iPhone and iPad platforms. In other words, in this case, Apple has determined a UI standard that will automatically be enforced. It doesn’t mean that popovers are impossible on an iPhone; it just means that in this particular case (action sheets), they’ll only work on the iPad.

Workshop

Quiz

1. Both alerts and action sheets require the use of which of the following?

a. UISheetController

b. UIDialogController

c. UIActionController

d. UIAlertController

2. Buttons in an alert or an action sheet correspond to which of the following?

a. UIButtonAction

b. UIAlertAction

c. UIAlertObject

d. UIAlertButton

3. Before using the kSystemSoundID_Vibrate constant, you must convert it to a what?

a. Boolean

b. String

c. UInt32

d. Float

4. Handlers are frequently provided in what form?

a. Structure

b. Block

c. Closet

d. Closure

5. What type of button is styled with red in an alert or action sheet?

a. Cancel

b. Default

c. Destructive

d. Erasure

6. After you’ve created an alert controller and configured it, you can display it with what method?

a. presentViewController

b. showViewController

c. displayViewController

d. useViewController

7. System Sound Services don’t play audio files directly. Instead, they require the use of what?

a. A sound ID

b. A sound path

c. A waveform

d. A sound descriptor

8. Alert controllers can display what to types of styles?

a. Alerts, warnings

b. Cautions, action sheets

c. Alerts, action sheets

d. Inputs, cautions

9. On some devices (just the iPad at the time of this writing), action sheets should be displayed with a what?

a. popoverController

b. popoverPresentationController

c. presentationController

d. presentationPopoverController

10. If you don’t want an alert action to do anything but dismiss the alert, what should you set the handler to?

a. AnyObject

b. null

c. nill

d. nil

Answers

1. D. Alert controllers (UIAlertController) are required when creating alerts and action sheets.

2. B. Define UIAlertActions to add functionality (and buttons!) to your alerts.

3. C. Unfortunately, you must convert the vibration constant to an unsigned 32-bit integer before you use it (UInt32).

4. D. Handlers are often implemented using a closure.

5. C. Destructive buttons are highlighted in red and should be used with any action that may result in data loss.

6. A. Use the presentViewController method to display alert controllers after they’ve been configured.

7. A. You must create a sound ID from an NSURL before you can play a sound resource.

8. C. Alerts and action sheets are the two visual styles currently available in the alert controller (UIAlertController).

9. B. When displaying an action sheet on the iPad, a popoverPresentationController is automatically created and should be used to display the sheet.

10. D. Use nil to set a handler that does nothing but dismiss the alert controller.

Activities

1. Practice adding text fields to a few alert controllers. Use a two-field alert to create a username and password dialog box.

2. Return to one or more of your earlier projects and add audio cues to the interface actions. Make switches click, buttons bing, and so on. Keep your sounds short, clear, and complementary to the actions that the users are performing.

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

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