How do I arrange SwiftUI Toolbar items?
Asked Answered
U

2

6

I have a SwiftUI ToolBar with 4 buttons, however the code I implemented is not correct because the buttons end up in weird places when changing the device type in simulator.

Even worse, when viewed on iPhone 8 / 8 Plus, 2 of the buttons are on the far edges of the window.

How do I properly apply spacing/padding to ToolBar buttons so they are consistent across different iOS devices?

Thank you!

IPhone 8

 // This code spaces the buttons but they change positions depending on the iOS device


                 ToolbarItem {
                        HStack {
                            HStack {
                                ProfileUploadMediaButton()
                            }.padding([.trailing], 85)
                            HStack {
                                ProfileUsernameButton()
                            }.padding([.trailing], 84)
                            HStack {
                                ProfileLiveButton()
                            }.padding([.trailing], 6)
                            HStack {
                                AccountButton()
                            }.padding([.trailing], 12)
                        }
                    }
                })

IPhone Pro Max

// I was thinking code like this but all buttons are bunched together on the right-side of  // the screen...

                    ToolbarItem {
                        HStack {
                            ProfileUploadMediaButton()
                            ProfileUsernameButton()
                            ProfileLiveButton()
                            AccountButton()
                        }
                    }

Items are all bunched together

Uintathere answered 5/2, 2021 at 22:4 Comment(1)
I'm not sure about this, but I think you can add some sort of spacing element in between each one. (I think it's called flexible space) Let me know if it works!Banville
I
11

When you add ToolbarItems, there is an initializer where you can explicitly set the placement of each item. For your case, you would add 3 ToolbarItems, for the left, center, and right. I'd mention that the toolbar is meant to be dynamic, so it may look different on different devices on purpose.

struct ToolbarView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
            }
            .navigationTitle("Test")
            .toolbar(content: {
                ToolbarItem(placement: .navigationBarLeading) {
                    Image(systemName: "camera.fill")
                }
                ToolbarItem(placement: .principal) {
                    Text("Username")
                }
                ToolbarItem(placement: .navigationBarTrailing) {
                    HStack {
                        Image(systemName: "dot.radiowaves.left.and.right")
                        Image(systemName: "heart.fill")
                    }
                }
            })
        }
    }

}

Per the documentation, here are the placement options. I'm guessing that when you don't explicitly add a placement, they default to .automatic.

  • automatic: The item is placed automatically, depending on many factors including the platform, size class, or presence of other items.

  • bottomBar: The item is placed in the bottom toolbar. Applies to iOS, iPadOS, and Mac Catalyst.

  • cancellationAction: The item represents a cancellation action for a modal interface.

  • confirmationAction: The item represents a confirmation action for a modal interface.

  • destructiveAction: The item represents a destructive action for a modal interface.

  • navigation: The item represents a navigation action.

  • navigationBarLeading: The item is placed in the leading edge of the navigation bar. Applies to iOS, iPadOS, tvOS, and Mac Catalyst.

  • navigationBarTrailing: The item is placed in the trailing edge of the navigation bar. Applies to iOS, iPadOS, tvOS, and Mac Catalyst.

  • primaryAction: The item represents a primary action.

  • principal: The item is placed in the principal item section.

  • ToolbarItemPlacement: The item represents a change in status for the current context.

Ira answered 5/2, 2021 at 22:30 Comment(1)
This approach doesn't work anymore. On Xcode 15.2 it causes error: Static method 'buildExpression' requires that 'ToolbarItem<(), some View>' conform to 'View'Stanislaw
D
0

I was able to modify nicksarno's answer slightly, essentially just converting it to use a trailing closure for content. The key for my problem was that every item in a .toolbar closure needs to be a ToolbarItem. You can't toss in a Spacer(), for example, without enclosing it in a ToolbarItem. Failure to enclose everything properly leads to failure to produce a diagnostic expression

import SwiftUI

struct ToolbarView: View {
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
            }
            .navigationTitle("Test")
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Text("Left Item")
                }
                
                ToolbarItem {
                    Spacer()
                }
                
                ToolbarItem(placement: .topBarTrailing) {
                    Text("Right Item")
                }
            }
        }
    }
}
Dockhand answered 1/5, 2024 at 18:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.