Reading time: 5 min

This recipe is a cheatsheet for various ToolbarItemPlacement values and combinations on iOS. This is useful because:

  1. The namings of the placement values don't necessarily clearly depict where will a ToolbarItem end up.
  2. Some values don't play well with each other. E.g, .primaryAction will hide .confirmationAction if it's placed above it, but not if placed below.
  3. Some positions change if TitleDisplayMode is .inline.

First we'll quickly introduce the toolbar modifier, and then we'll look at how do various item placements and their combinations look on iOS.

TL;DR Here are the links to the bottom bar summary and navigation bar summary.

Intro to Toolbars

iOS 14 introduced the toolbar modifier, allowing you to add ToolbarItems to either the toolbar (bottom bar) or the navigation bar. Note that this only works for views that are embedded in a NavigationView (either directly, or become via the navigation stack).

Here's a simple example that adds a few buttons around:

Note: The example uses the blueNavigation modifier from this recipe on Navigation Bar Styling.

struct ToolbarTestView: View {
  var body: some View {
    NavigationView { // 1
      Text("Hello, World!")
        .padding()
        .navigationTitle("Toolbar Tester")
        .blueNavigation
        .toolbar { // 2
          ToolbarItem(placement: .navigation) { // 3
            Button("Navigation") { } // 4
          }
          ToolbarItem(placement: .bottomBar) { // 3
            Button("BottomBar") { } // 4
          }
        }
    }
  }
}

What happens here is:

  1. Views that use toolbar must be embedded in NavigationView. Alternatively, the view from which you navigated to via NavigationLink should be.
  2. Use the same toolbar modifier for all items, regardless of if they're in the navigation bar or bottom bar.
  3. Use the ToolbarItem wrapper to add items to the bar and specify the position with placement:.
  4. Add a regular Button with a title and empty action.

The result looks like this. You can see two buttons, one added to the navigation bar and one to the bottom toolbar:

Screenshot%202021-04-19%20at%2009.13.46

Okay, time to dive into all the placements and their combos!

ToolbarItemPlacement and its values

The ToolbarItemPlacement is a struct with a bunch of static constants representing different toolbar placements (you can think of it as an enum, even though it's not implemented that way). Here are all the supported placement values:

  • automatic
  • principal
  • navigation
  • primaryAction
  • status
  • confirmationAction
  • cancellationAction
  • destructiveAction
  • navigationBarLeading
  • navigationBarTrailing
  • bottomBar

We'll look at the placements that affect the bottom space (bottom toolbar) and the top space (navigation bar) separately, as they don't affect each other.

Fot the sake of brevity, example will just show the code inside the toolbar modifier block.

Bottom bar placements

The bottom bar placements are status and bottomBar.

ToolbarItem(placement: ToolbarItemPlacement.status) {
  Button("Status") { }
}
ToolbarItem(placement: ToolbarItemPlacement.bottomBar) {
  Button("BottomBar") { }
}

Screenshot%202021-04-19%20at%2009.36.01

  • status is placed in the center of the toolbar.
  • bottomBar starts from the leading edge of the toolbar.

If you add another bottomBar to the list:

ToolbarItem(placement: ToolbarItemPlacement.bottomBar) {
  Button("BottomBar2") { }
}

it goes to the trailing edge:

Screenshot%202021-04-19%20at%2009.42.19

If you keep adding more bottomBar items:

ToolbarItem(placement: ToolbarItemPlacement.bottomBar) {
  Button("BottomBar3") { }
}

they'll keep lining up at the trailing edge:

Screenshot%202021-04-19%20at%2009.45.00

Multiple status items:

ToolbarItem(placement: ToolbarItemPlacement.status) {
  Button("Status") { }
}
ToolbarItem(placement: ToolbarItemPlacement.status) {
  Button("Status2") { }
}

are just sequentially lined up in the center (if possible):

Screenshot%202021-04-19%20at%2009.47.38

Screenshot%202021-04-19%20at%2009.47.25

Summary:

  1. status items(s) are placed in the center.
  2. First bottomBar items goes to the leading edge.
  3. Other bottomBar items go to the trailing edge.
  4. The mix sort of works as you would expect.
Navigation bar placements

Things get a bit more complicated with the navigation bar placements, as there are many overlapping ones:

  • automatic, primaryAction, confirmationAction, destructiveAction and navigationBarTrailing all compete for the navigation bar trailing position.
  • navigation, cancellationAction and navigationBarLeading compete for the navigation bar leading position.
  • principal is the only one placed in the center of the navigation bar.

Here's a quick code to illustrate where do these placements live:

ToolbarItem(placement: ToolbarItemPlacement.automatic) {
  Button("Automatic") { }
}
ToolbarItem(placement: ToolbarItemPlacement.principal) {
  Button("Principal") { }
}
ToolbarItem(placement: ToolbarItemPlacement.navigation) {
  Button("Navigation") { }
}

Screenshot%202021-04-19%20at%2010.03.45

Here's where things get more than a little fuzzy. If you line up all the trailing placement options, like this:

ToolbarItem(placement: ToolbarItemPlacement.automatic) {
  Button("Automatic") { }
}
ToolbarItem(placement: ToolbarItemPlacement.navigationBarTrailing) {
  Button("NavigationBarTrailing") { }
}
ToolbarItem(placement: ToolbarItemPlacement.primaryAction) {
  Button("PrimaryAction") { }
}
ToolbarItem(placement: ToolbarItemPlacement.confirmationAction) {
  Button("ConfirmationAction") { }
}
ToolbarItem(placement: ToolbarItemPlacement.destructiveAction) {
  Button("DestructiveAction") { }
}

you'll only get the automatic one:

Screenshot%202021-04-19%20at%2010.07.41

If you place the primaryAction above navigation, then "Primary" goes to top.

However, if you place navigationBarTrailing above primaryAction, you get something like this:

Screenshot%202021-04-19%20at%2010.09.52

Moral of the story is don't mix competing modifiers together - pick one and stick with it. The combination of placements and their ordering (which item is placed above the other in code) both seem to matter and the results are just weird.

The same is true for leading placement options.

ToolbarItem(placement: ToolbarItemPlacement.navigation) {
  Button("Navigation") { }
}
ToolbarItem(placement: ToolbarItemPlacement.cancellationAction) {
  Button("CancellationAction") { }
}
ToolbarItem(placement: ToolbarItemPlacement.navigationBarLeading) {
  Button("NavigationBarLeading") { }
}

navigation on top eats up everything:

Screenshot%202021-04-19%20at%2010.13.16

But if it goes to the bottom:

ToolbarItem(placement: ToolbarItemPlacement.cancellationAction) {
  Button("CancellationAction") { }
}
ToolbarItem(placement: ToolbarItemPlacement.navigationBarLeading) {
  Button("NavigationBarLeading") { }
}

ToolbarItem(placement: ToolbarItemPlacement.navigation) {
  Button("Navigation") { }
}

you get all the buttons:

Screenshot%202021-04-19%20at%2010.13.31

Also, note that having more than one navigation, principal or automatic item has no effect - it always only displays the top one. Multiplying items works for other placement types.

For the last test, let's use the inline navigation bar title. Change the navigationTitle line to:

.navigationBarTitle("Toolbar Tester", displayMode: .inline)

With a navigation and confirmationAction items, it looks like this:

Screenshot%202021-04-19%20at%2010.19.01

However, if you add a principal, it covers up the title:

Screenshot%202021-04-19%20at%2010.19.11

Summary:

  1. Trailing - automatic, primaryAction, confirmationAction, destructiveAction and navigationBarTrailing.
  2. Leading - navigation, cancellationAction and navigationBarLeading.
  3. Principal - center.
  4. automatic and navigation cover up all the other placements in equal positioning.
  5. automatic, navigation and principal don't allow duplication, other placement options do.
  6. confirmationAction has default bold text.
  7. In the inline display mode, principal replaces the title.
  8. Generally, don't mix these placement options together. Pick one for leading/trailing and stick with it.

Next Post Previous Post