The Input Accessory View


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

Native Accessory View
The native iOS input accessory view

This post walks through how to add the area above the keyboard — known as an input accessory view — to UITextFields in your project. The finished product looks like this:

Demo
Previous, next, and done navigation between text fields

Ready? Open Xcode, select File > New > Project, and choose Single View Application. This tutorial uses Objective-C.

Save Dialog
Name your project

Set Up the Storyboard

Open the Storyboard and drag four text fields onto the canvas. In the Attributes Inspector, set optional placeholder values ("Text field 1," "Text field 2," etc.). Change the first 3 text fields' return key to "Next" and the last one to "Done." Enable "Auto-enable Return Key" to require input before advancing.

Return Key Screenshot
Set the return key type in the Attributes Inspector

Set the tag parameter for each text field (values 0–3). This lets us differentiate between fields in code via textField.tag.

Resolve auto layout by using each view's suggested constraints:

Reset to Suggested Constraints
Reset to suggested constraints
Storyboard State
The Storyboard at this stage

Create an Outlet Collection

Open the Assistant Editor:

Assistant Editor
The Assistant Editor

Command-drag from a text field to the area between @interface and @end. Choose "Outlet Collection" as the connection type and name it textFields. Then Command-drag each remaining text field to the same collection.

Create Outlet Collection
Creating an IBOutletCollection

The Code

Under the textFields array, declare a UITextField to track the active field and an NSArray to hold the accessory views:

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

Write a setter for activeTextField:

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

Add navigation methods that use the active field's tag:

- (void)goToPrevField {
    [[_textFields objectAtIndex:(_activeTextField.tag - 1)] becomeFirstResponder];
}

- (void)goToNextField {
    [[_textFields objectAtIndex:(_activeTextField.tag + 1)] becomeFirstResponder];
}

- (void)dismissKeyboard {
    [[_textFields objectAtIndex:_activeTextField.tag] resignFirstResponder];
}

Create the setupInputAccessoryViews method to build four toolbars — one per text field — each containing previous, next, and done buttons:

- (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)];
        UIBarButtonItem *nextButton  = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:102 target:nil action:@selector(goToNextField)];
        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 prev on first field, next on last
    ((UIBarButtonItem *)[((UIToolbar *)[_inputAccessoryViews objectAtIndex:0]).items objectAtIndex:0]).enabled = NO;
    ((UIBarButtonItem *)[((UIToolbar *)[_inputAccessoryViews objectAtIndex:3]).items objectAtIndex:2]).enabled = NO;
}

In viewDidLoad, call setupInputAccessoryViews and assign the views:

[self setupInputAccessoryViews];

for(UITextField *field in _textFields) {
    [field addTarget:self action:@selector(setActiveTextField:) forControlEvents:UIControlEventEditingDidBegin];
    [field setInputAccessoryView:[_inputAccessoryViews objectAtIndex:field.tag]];
}

Build and run — you can now navigate through the text fields using the accessory view buttons.

Making the Return Key Work

Assign each text field's delegate to the View Controller:

Assign UITextField Delegate
Assigning the UITextFieldDelegate

Then implement textFieldShouldReturn::

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (textField.tag < 3) {
        [[_textFields objectAtIndex:(textField.tag + 1)] becomeFirstResponder];
    } else if (textField.tag == 3) {
        [[_textFields objectAtIndex:textField.tag] resignFirstResponder];
    }
    return YES;
}

The full source is available on GitHub.