8

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

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! Commented Feb 5, 2021 at 22:08

2 Answers 2

13

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: .topBarLeading) {
                    Image(systemName: "camera.fill")
                }
                ToolbarItem(placement: .principal) {
                    Text("Username")
                }
                ToolbarItem(placement: .topBarTrailing) {
                    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.

Sign up to request clarification or add additional context in comments.

2 Comments

This approach doesn't work anymore. On Xcode 15.2 it causes error: Static method 'buildExpression' requires that 'ToolbarItem<(), some View>' conform to 'View'
.navigationBarLeading and .navigationBarTrailing is deprecated, so I updated the answer with a replacement one.
1

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")
                }
            }
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.