Managing the Keyboard

When users touch a text field, a text view, or a field in a web view, the system displays a keyboard. You can configure the type of keyboard that is displayed along with several attributes of the keyboard. You also have to manage the keyboard when the editing session begins and ends. Because the keyboard could hide the portion of your view that is the focus of editing, this management might include adjusting the user interface to raise the area of focus so that is visible above the keyboard.

Keyboards and Input Methods

Whenever the user taps in an object capable of accepting text input, the object asks the system to display an appropriate keyboard. Depending on the needs of your program and the user’s preferred language, the system might display one of several different keyboards. Although your app cannot control the user’s preferred language (and thus the keyboard’s input method), it can control attributes of the keyboard that indicate its intended use, such as the configuration of any special keys and its behaviors.

Configuring the Keyboard for Text Objects

You configure the attributes of the keyboard directly through the text objects of your app. The UITextField and UITextView classes both conform to the UITextInputTraits protocol, which defines the properties for configuring the keyboard. Setting these properties programmatically or in the Interface Builder inspector window causes the system to display the keyboard of the designated type.

The default keyboard configuration is designed for general text input. Figure 5-1 displays the default keyboard along with several other keyboard configurations. The default keyboard displays an alphabetical keyboard initially but the user can toggle it and display numbers and punctuation as well. Most of the other keyboards offer similar features as the default keyboard but provide additional buttons that are specially suited to particular tasks. However, the phone and numerical keyboards offer a dramatically different layout that is tailored towards numerical input.

Figure 5-1  Several different keyboard types
Several different keyboard types

To implement the language preferences of different users, iOS also supports different input methods and keyboard layouts for different languages, some of which are shown in Figure 5-2. The input method and layout for the keyboard is determined by the user’s language preferences. Input for some of these keyboards takes place in multiple stages.

Figure 5-2  Several different keyboards and input methods
Several different keyboards and input methods

Configuring the Keyboard for Web Views

Although the UIWebView class does not support the UITextInputTraits protocol directly, you can configure some keyboard attributes for text input elements. For example, you can include autocorrect and autocapitalize attributes in the definition of an input element to specify the keyboard’s behaviors, as shown in the following example.

<input type="text" size="30" autocorrect="off" autocapitalize="on">

You can also control which type of keyboard is displayed when a user touches a text field in a web page. To display a telephone keypad, an email keyboard, or a URL keyboard, use the tel, email, or url keywords for the type attribute on an input element, respectively. To display a numeric keyboard, set the value of the pattern attribute to "[0-9]*" or "\d*".

These keywords and the pattern attribute are part of HTML 5 and are available in iOS. The following list shows how to display each type of keyboard, including the standard keyboard.

  • Text: <input type="text"></input>

  • Telephone: <input type="tel"></input>

  • URL: <input type="url"></input>

  • Email: <input type="email"></input>

  • Zip code: <input type="text" pattern="[0-9]*"></input>

Managing the Keyboard

Although many UIKit objects display the keyboard automatically in response to user interactions, your app still has some responsibilities for configuring and managing the keyboard. The following sections describe those responsibilities.

Receiving Keyboard Notifications

When the keyboard is shown or hidden, iOS sends out the following notifications to any registered observers:

Each keyboard notification includes information about the size and position of the keyboard on the screen. You can access this information from the userInfo dictionary of each notification using the UIKeyboardFrameBeginUserInfoKey and UIKeyboardFrameEndUserInfoKey keys; the former gives the beginning keyboard frame, the latter the ending keyboard frame (both in screen coordinates). You should always use the information in these notifications as opposed to assuming the keyboard is a particular size or in a particular location. The size of the keyboard is not guaranteed to be the same from one input method to another and may also change between different releases of iOS. In addition, even for a single language and system release, the keyboard dimensions can vary depending on the orientation of your app. For example, Figure 5-3 shows the relative sizes of the URL keyboard in both the portrait and landscape modes. Using the information inside the keyboard notifications ensures that you always have the correct size and position information.

Figure 5-3  Relative keyboard sizes in portrait and landscape modes
Relative keyboard sizes in portrait and landscape modes

One reason to use keyboard notifications is so that you can reposition content that is obscured by the keyboard when it is visible. For information on how to handle this scenario, see “Moving Content That Is Located Under the Keyboard.”

There is no defined relationship between the timing of keyboard notifications and the timing of view-controller transitions.

Displaying the Keyboard

When the user taps a view, the system automatically designates that view as the first responder. When this happens to a view that contains editable text, the view initiates an editing session for that text. At the beginning of that editing session, the view asks the system to display the keyboard, if it is not already visible. If the keyboard is already visible, the change in first responder causes text input from the keyboard to be redirected to the newly tapped view.

Because the keyboard is displayed automatically when a view becomes the first responder, you often do not need to do anything to display it. However, you can programmatically display the keyboard for an editable text view by calling that view’s becomeFirstResponder method. Calling this method makes the target view the first responder and begins the editing process just as if the user had tapped on the view.

If your app manages several text-based views on a single screen, it is a good idea to track which view is currently the first responder so that you can dismiss the keyboard later.

Dismissing the Keyboard

Although it typically displays the keyboard automatically, the system does not dismiss the keyboard automatically. Instead, it is your app’s responsibility to dismiss the keyboard at the appropriate time. Typically, you would do this in response to a user action. For example, you might dismiss the keyboard when the user taps the Return or Done button on the keyboard or taps some other button in your app’s interface. Depending on how you configured the keyboard, you might need to add some additional controls to your user interface to facilitate the keyboard’s dismissal.

To dismiss the keyboard, you call the resignFirstResponder method of the text-based view that is currently the first responder. When a text view resigns its first responder status, it ends its current editing session, notifies its delegate of that fact, and dismisses the keyboard. In other words, if you have a variable called myTextField that points to the UITextField object that is currently the first responder, dismissing the keyboard is as simple as doing the following:

[myTextField resignFirstResponder];

Everything from that point on is handled for you automatically by the text object.

Moving Content That Is Located Under the Keyboard

When asked to display the keyboard, the system slides it in from the bottom of the screen and positions it over your app’s content. Because it is placed on top of your content, it is possible for the keyboard to be placed on top of the text object that the user wanted to edit. When this happens, you must adjust your content so that the target object remains visible.

Adjusting your content typically involves temporarily resizing one or more views and positioning them so that the text object remains visible. The simplest way to manage text objects with the keyboard is to embed them inside a UIScrollView object (or one of its subclasses like UITableView). When the keyboard is displayed, all you have to do is reset the content area of the scroll view and scroll the desired text object into position. Thus, in response to a UIKeyboardDidShowNotification, your handler method would do the following:

  1. Get the size of the keyboard.

  2. Adjust the bottom content inset of your scroll view by the keyboard height.

  3. Scroll the target text field into view.

Figure 5-4 illustrates the preceding steps for a simple app that embeds several text fields inside a UIScrollView object. When the keyboard appears, the notification handler method adjusts the content and scroll indicator insets of the scroll view and then uses the scrollRectToVisible:animated: method of UIScrollView to scroll the tapped text field (in this case the email field) into view.

Figure 5-4  Adjusting content to accommodate the keyboard
Adjusting content to accommodate the keyboard

Listing 5-1 shows the code for registering to receive keyboard notifications and shows the handler methods for those notifications. This code is implemented by the view controller that manages the scroll view, and the scrollView variable is an outlet that points to the scroll view object. The keyboardWasShown: method gets the keyboard size from the info dictionary of the notification and adjusts the bottom content inset of the scroll view by the height of the keyboard. It also sets the scrollIndicatorInsets property of the scroll view to the same value so that the scrolling indicator won’t be hidden by the keyboard. Note that the keyboardWillBeHidden: method doesn’t use the keyboard size; it simply sets the scroll view’s contentInset and scrollIndicatorInsets properties to the default value, UIEdgeInsetsZero.

If the active text field is hidden by the keyboard, the keyboardWasShown: method adjusts the content offset of the scroll view appropriately. The active field is stored in a custom variable (called activeField in this example) that is a member variable of the view controller and set in the textFieldDidBeginEditing: delegate method, which is itself shown in Listing 5-2. (In this example, the view controller also acts as the delegate for each of the text fields.)

Listing 5-1  Handling the keyboard notifications

// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
            selector:@selector(keyboardWasShown:)
            name:UIKeyboardDidShowNotification object:nil];
 
   [[NSNotificationCenter defaultCenter] addObserver:self
             selector:@selector(keyboardWillBeHidden:)
             name:UIKeyboardWillHideNotification object:nil];
 
}
 
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
 
    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;
 
    // If active text field is hidden by keyboard, scroll it so it's visible
    // Your app might not need or want this behavior.
    CGRect aRect = self.view.frame;
    aRect.size.height -= kbSize.height;
    if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) {
        [self.scrollView scrollRectToVisible:activeField.frame animated:YES];
    }
}
 
// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    scrollView.contentInset = contentInsets;
    scrollView.scrollIndicatorInsets = contentInsets;
}

Listing 5-2 shows some additional code used by the view controller to set and clear the activeField variable in the preceding example. During initialization, each text field in the interface sets the view controller as its delegate. Therefore, when a text field becomes active, it calls these methods. For more information on text fields and their delegate notifications, see “Managing Text Fields and Text Views.”

Listing 5-2  Additional methods for tracking the active text field.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}
 
- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

There are other ways you can scroll the edited area in a scroll view above an obscuring keyboard. Instead of altering the bottom content inset of the scroll view, you can extend the height of the content view by the height of the keyboard and then scroll the edited text object into view. Although the UIScrollView class has a contentSize property that you can set for this purpose, you can also adjust the frame of the content view, as shown in Listing 5-3. This code also uses the setContentOffset:animated: method to scroll the edited field into view, in this case scrolling it just above the top of the keyboard.

Listing 5-3  Adjusting the frame of the content view and scrolling a field above the keyboard

- (void)keyboardWasShown:(NSNotification*)aNotification {
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
    CGRect bkgndRect = activeField.superview.frame;
    bkgndRect.size.height += kbSize.height;
    [activeField.superview setFrame:bkgndRect];
    [scrollView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES];
}