The Input Accessory View


Have you ever wondered how to add the previous, next, and done buttons (highlighted below) to the keyboard in your iOS app?

Native Accessory View

This post will walk you through how to add the area above the keyboard, known as an input accessory view, to the UITextFields in your project to achieve this functionality. The finished product will look like this:

Demo

Pretty sweet, right? Ready to get started?

First, open XCode, select File>New>Project… and select Single View Application in the window that appears. Click Next and name the project. This tutorial uses Objective-C. Click Next and specify a location to save the project.

Save Dialog

Open the Storyboard and drag four text fields onto the canvas. Next, open the Attributes Inspector, select the text fields, one at a time, and specify optional placeholder values for the text fields. I chose “Text field [1, 2, 3, 4].” The “Return Key” parameter in the Attributes Inspector changes the text that will occupy the return key (shocker).

Change the first 3 text fields’ return key to “Next” and the last text field’s return key to “Done.” Enabling the “Auto-enable Return key” parameter forces the user to input a value before enabling the return button (Next or Done).

We will later write the code to make these buttons function as you would expect.

Return Key Screenshot

Next, specify the tag parameter for each of the text fields. I will use values 0-3. This will allow us to differentiate between the text fields in code by referencing their tag value: textField.tag. Before we leave the Storyboard, we should resolve auto layout issues.

Thankfully, our UI is very simple, and we can easily use each views’ suggested constraints to make this app’s UI functional on all iOS platforms.

Reset to Suggested Constraints Demo

At this point, the Storyboard should look something like this:

Storyboard State

Open the Assistant Editor (selected in the screenshot below), and ViewController.m should automatically appear.

Assistant Editor

Command-drag from a text field to the area under @interface and above @end. Specify Outlet Collection as the connection type, and name the collection textFields. This will create an IBOutletCollection of UITextFields.

Create Outlet Collection

Command-drag each of the remaining text fields to the textFields Outlet Collection.

Now for the exciting part–the code!

Under the textFields array we just created, make a UITextField called activeTextField to monitor which field is currently selected, and create an NSArray called inputAccessoryViews to hold the accessory views that we will be adding to the UITextFields later.

@property (strong, nonatomic) UITextField *activeTextField;
@property (strong, nonatomic) NSArray *inputAccessoryViews;

Write a setter method for activeTextField, which we will use later to assign the activeTextField to the currently selected field.

- (void)setActiveTextField:(UITextField *)activeTextField {
    _activeTextField = activeTextField;
}

Next, write the methods to go to the next text field, previous text field, and to dismiss the keyboard (for the Done button). These methods use the activeTextField’s tag to reference the adjacent UITextFields.

//
// Focus on the previous UITextField
//
- (void)goToPrevField {
    [[_textFields objectAtIndex:(_activeTextField.tag - 1)] becomeFirstResponder];
}

//
// Focus on the next UITextField
//
- (void)goToNextField {
    [[_textFields objectAtIndex:(_activeTextField.tag + 1)] becomeFirstResponder];
}

//
// Dismiss the keyboard when done is tapped.
//
- (void)dismissKeyboard {
    [[_textFields objectAtIndex:_activeTextField.tag] resignFirstResponder];
}

An input accessory view is a UIView that contains UIBarButtonItems. Create a method, setupInputAccessoryViews, to setup the four input accessory views (one for each UITextField) by iterating through each of them and adding UIBarButtonItems.

Each view will have a previous button, a next button, and a done button, which use a Selector to call the methods we wrote earlier. They will also include a flexible space between the next and done buttons, which will right justify the Done button. To provide a space between the previous and next buttons, use a plain UIBarButtonItem called placeholder and place it in between the prevButton and nextButton.

//
// Create the 4 accessory views for each of the the UITextFields, each containing
// a previous button, a next button, and a done button.
//
- (void)setupInputAccessoryViews {
    _inputAccessoryViews = [[NSArray alloc] initWithObjects:[[UIToolbar alloc] init], [[UIToolbar alloc] init], [[UIToolbar alloc] init], [[UIToolbar alloc] init], nil];

    for(UIToolbar *accessoryView in _inputAccessoryViews) {
        UIBarButtonItem *prevButton  = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:101 target:nil action:@selector(goToPrevField)]; // 101 is the < character
        UIBarButtonItem *nextButton  = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:102 target:nil action:@selector(goToNextField)]; // 102 is the > character
        UIBarButtonItem *doneButton  = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStylePlain target:nil action:@selector(dismissKeyboard)];
        UIBarButtonItem *flexSpace   = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
        UIBarButtonItem *placeholder = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

        [accessoryView sizeToFit];
        [accessoryView setItems:[NSArray arrayWithObjects: prevButton, placeholder, nextButton, placeholder, flexSpace, placeholder, doneButton, nil] animated:YES];
    }

    // disable the previous button in the first accessory view
    ((UIBarButtonItem*)[((UIToolbar*)[_inputAccessoryViews objectAtIndex:0]).items objectAtIndex:0]).enabled = NO;
    // disable the next button in the last accessory view
    ((UIBarButtonItem*)[((UIToolbar*)[_inputAccessoryViews objectAtIndex:3]).items objectAtIndex:2]).enabled = NO;
}

Now we can call setupInputAccessoryViews and assign them to our NSArray of textFields in the viewDidLoad method. We also add a Selector to each of the fields to call the setActiveTextField method when the fields are touched. Selectors are passed the sender(the UITextField) as a parameter.

[self setupInputAccessoryViews];

// monitor which field is currently selected, and set each of the input accessory views.
for(UITextField *field in _textFields) {
    [field addTarget:self action:@selector(setActiveTextField:) forControlEvents:UIControlEventEditingDidBegin];
    [field setInputAccessoryView:[_inputAccessoryViews objectAtIndex:field.tag]];
}

Hurrah! You can now build and run the application and will be able to use the keyboard’s input accessory view to navigate through the UITextFields in the project.

If you would like the return buttons (Next/Done) to function properly, you have to do two more small steps. First, assign each of the the UITextFields’ delegates to the View Controller, like so:

Assign UITextField's delegate

Next, provide the implementation for (BOOL)textFieldShouldReturn:(UITextField *)textField in the view controller, which will be called when the user touches the return key.

We use the UITextField‘s tag to differentiate between each field. If the user is focused on the first three text fields, we tell the application to focus on the next field. Otherwise (the user is focused on the last text field), dismiss the keyboard.

//
// Controls behavior of touching the Next or Done button in the keyboard.
//
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    // if currently focused on first three text fields, go to the next text field
    if (textField.tag < 3) {
        [[_textFields objectAtIndex:(textField.tag + 1)] becomeFirstResponder];
        // if currently focused on last text field, dismiss the keyboard.
    } else if (textField.tag == 3) {
        [[_textFields objectAtIndex:textField.tag] resignFirstResponder];
    }

    return YES;
}

The full source of this project is available on Github.