UIToolBar
iOS development
toolbar alignment
user interface design
Xcode

Aligning UIToolBar items

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UIToolbar in UIKit positions its UIBarButtonItem items in a horizontal row, and alignment is controlled by inserting special spacer items between buttons. A flexibleSpace item expands to fill available space (pushing items apart), while a fixedSpace item adds an exact pixel gap. By combining these spacers with your action items, you can left-align, center, or right-align buttons — or spread them evenly across the toolbar. Since iOS 14, you can also use UIBarButtonItem with SF Symbols and menu support.

Basic Toolbar Setup

swift
1class ViewController: UIViewController {
2    override func viewDidLoad() {
3        super.viewDidLoad()
4
5        let toolbar = UIToolbar()
6        toolbar.translatesAutoresizingMaskIntoConstraints = false
7        view.addSubview(toolbar)
8
9        NSLayoutConstraint.activate([
10            toolbar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
11            toolbar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
12            toolbar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
13        ])
14
15        let addButton = UIBarButtonItem(
16            barButtonSystemItem: .add, target: self, action: #selector(addTapped))
17        let editButton = UIBarButtonItem(
18            barButtonSystemItem: .edit, target: self, action: #selector(editTapped))
19
20        toolbar.items = [addButton, editButton]
21    }
22
23    @objc func addTapped() { print("Add") }
24    @objc func editTapped() { print("Edit") }
25}

Without spacers, items are packed together on the left side of the toolbar.

Right-Aligning Items

Place a flexibleSpace before the items to push them to the right:

swift
1let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
2                                 target: nil, action: nil)
3let doneButton = UIBarButtonItem(
4    barButtonSystemItem: .done, target: self, action: #selector(doneTapped))
5
6toolbar.items = [flexSpace, doneButton]
7// [         flexible space         ] [Done]

This is the most common pattern — a Done button right-aligned on a toolbar above a keyboard.

Centering Items

Place flexible spaces on both sides:

swift
1let flexLeft = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
2                                target: nil, action: nil)
3let flexRight = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
4                                 target: nil, action: nil)
5let titleItem = UIBarButtonItem(title: "Page 1 of 5",
6                                 style: .plain, target: nil, action: nil)
7titleItem.isEnabled = false  // Non-tappable label
8
9toolbar.items = [flexLeft, titleItem, flexRight]
10// [   flex   ] [Page 1 of 5] [   flex   ]

The two flexible spaces expand equally, centering the title.

Evenly Distributed Items

Place flexible spaces between every item:

swift
1let flex = { UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
2                              target: nil, action: nil) }
3
4let camera = UIBarButtonItem(barButtonSystemItem: .camera,
5                              target: self, action: #selector(cameraTapped))
6let compose = UIBarButtonItem(barButtonSystemItem: .compose,
7                               target: self, action: #selector(composeTapped))
8let share = UIBarButtonItem(barButtonSystemItem: .action,
9                             target: self, action: #selector(shareTapped))
10
11toolbar.items = [flex(), camera, flex(), compose, flex(), share, flex()]
12// [flex][Camera][flex][Compose][flex][Share][flex]

Each flexible space gets an equal share of the remaining width, distributing buttons evenly.

Fixed Spacing Between Items

Use fixedSpace for precise pixel gaps:

swift
1let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace,
2                                  target: nil, action: nil)
3fixedSpace.width = 20  // 20-point gap
4
5let boldButton = UIBarButtonItem(title: "B", style: .plain,
6                                  target: self, action: #selector(boldTapped))
7let italicButton = UIBarButtonItem(title: "I", style: .plain,
8                                    target: self, action: #selector(italicTapped))
9
10toolbar.items = [boldButton, fixedSpace, italicButton]

Toolbar Above Keyboard (Input Accessory View)

A common use case is adding a toolbar above the keyboard:

swift
1func createKeyboardToolbar() -> UIToolbar {
2    let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
3
4    let prevButton = UIBarButtonItem(title: "Prev", style: .plain,
5                                      target: self, action: #selector(prevField))
6    let nextButton = UIBarButtonItem(title: "Next", style: .plain,
7                                      target: self, action: #selector(nextField))
8    let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace,
9                                      target: nil, action: nil)
10    fixedSpace.width = 12
11    let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
12                                     target: nil, action: nil)
13    let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
14                                      target: self, action: #selector(dismissKeyboard))
15
16    toolbar.items = [prevButton, fixedSpace, nextButton, flexSpace, doneButton]
17    // [Prev] [12pt] [Next] [     flex     ] [Done]
18    return toolbar
19}
20
21// Attach to a text field
22textField.inputAccessoryView = createKeyboardToolbar()

Using UINavigationController's Built-in Toolbar

UINavigationController has a built-in toolbar you can show without creating one manually:

swift
1// In your UINavigationController setup
2navigationController?.isToolbarHidden = false
3
4// In each view controller, set toolbarItems
5override func viewDidLoad() {
6    super.viewDidLoad()
7
8    let flex = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
9                                target: nil, action: nil)
10    let refresh = UIBarButtonItem(barButtonSystemItem: .refresh,
11                                   target: self, action: #selector(refreshTapped))
12    let trash = UIBarButtonItem(barButtonSystemItem: .trash,
13                                 target: self, action: #selector(trashTapped))
14
15    toolbarItems = [refresh, flex, trash]
16}

Each view controller can provide its own toolbarItems, and they update automatically during navigation transitions.

Common Pitfalls

  • Forgetting to set the toolbar frame for input accessory views: When used as inputAccessoryView, the toolbar needs an explicit frame height (typically 44 points). Without it, the toolbar may have zero height and be invisible above the keyboard.
  • Creating a single spacer instance and reusing it in the items array: Each position in the items array needs its own UIBarButtonItem instance. Reusing the same flexible space object at multiple positions causes layout issues because UIKit tracks each item's position individually.
  • Not using safeAreaLayoutGuide for bottom toolbar placement: On devices with a home indicator (iPhone X and later), constraining the toolbar to view.bottomAnchor instead of view.safeAreaLayoutGuide.bottomAnchor causes the toolbar to extend behind the home indicator, making bottom buttons hard to tap.
  • Setting toolbar.items before adding the toolbar to the view hierarchy: While this usually works, some layout calculations may not apply correctly. Set items after the toolbar is part of the view hierarchy and constraints are activated.
  • Mixing setItems(_:animated:) with direct items assignment: Use setItems(_:animated: true) for animated transitions between toolbar configurations. Direct items assignment does not animate, which can look jarring when swapping button sets.

Summary

  • Use flexibleSpace bar button items to push toolbar items apart — one on the left for right-alignment, one on each side for centering
  • Use fixedSpace with a set width for precise pixel gaps between items
  • Place flexible spaces between every item for even distribution
  • For keyboard toolbars, create a UIToolbar with an explicit frame and assign it to textField.inputAccessoryView
  • Use UINavigationController's built-in isToolbarHidden and toolbarItems instead of managing a separate toolbar when inside a navigation controller
  • Each spacer item must be a separate instance — do not reuse the same UIBarButtonItem object at multiple positions

Course illustration
Course illustration

All Rights Reserved.