UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: .navigationBarItems alignment changes when size of View changes

Forums > SwiftUI

I've been having some issues with the alignment of my .navigationBarItems in SwiftUI. See example code below:

struct ContentView: View {
    @State private var showingLongTextView = true

    var body: some View {
        NavigationView {
            Text("Hello world!")
            .navigationBarItems(leading:
                Button(action: { self.showingLongTextView.toggle() }) {
                    Text(showingLongTextView ? "A very very long text view" : "Short view")
                }
            )
        }
    }
}

Whenever the Text in the button above changes from long to short, the shorter Text View will no longer stay aligned to the left. The only solution I've found is to give the Text a specific frame width, and an alignment of .leading as shown below. Note: I've added a border as well to illustrate the clickable area of the button.

Text(showingLongTextView ? "A very very long text view" : "Short view")
                        .fixedSize()
                        .frame(width: 100, alignment: .leading)
                        .border(Color.blue)

This is not ideal because I want the clickable area to always conform to the size of the text, but also maintaining a .leading alignment no matter the size of the TextView.

Does anyone know of a solution to this problem? I've tried VStack, HStack, and Geometry reader but I've yet to find a solution. Any help would be greatly appreciated.

Thanks, Zoyd

3      

Here's a simplified version. For some reason I couldn't get the image to post correctly. Pasting the code into a project will display the problem clearly:

struct ContentView: View {
    @State private var showingLongTextView = true

    var body: some View {
        NavigationView {
            ZStack {
                textView()
                    .border(Color.black)
                    .frame(width: 300, height: 300, alignment: .topLeading)
                    .border(Color.black)
                Button("Toggle Views", action: { self.showingLongTextView.toggle()})
            }

            .navigationBarItems(leading: textView().border(Color.black))
        }
    } //body

    func textView() -> Text {
        if showingLongTextView {
            return Text("A very very long text View")
        } else {
            return Text("Short view")
        }
    }
}

3      

Hi Zoyd. I had a look at this a while ago. I think people have looked and not found a solution. But will try to explain what I think happening

If you change to having the short text first then the long text it will show first time as what you want to happen, however when go back to the short text you have the same problem. There are number of things going on when you go to the long view. The title move over(put title in your code and see), this do not return to centre. I think complier put left,centre,right items to where best fit.

First why do you have a long text in NavBar?

Taken from Human Inferface Guidelines

Avoid crowding a navigation bar with too many controls. In general, a navigation bar should contain no more than the view’s current title, a back button, and one control that manages the view’s contents. If you use a segmented control in the navigation bar, the bar shouldn’t include a title or any controls other than the segmented control.

Human Inferface Guidelines

3      

Thanks for the reply Nigel.

I stumbled upon this while working on one of the challenges for the Moonshot program in 100 Days of SwiftUI and became fixated on it. You're absolutely right about the title being moved over if the View is too large. I didn't notice it because my title only moved up to the top when scrolling down. Changing the title to .inline exhibits the problem you described.

.navigationBarTitle("Moonshot", displayMode: .inline)
            .navigationBarItems(trailing:
                Button(action: { self.showingLaunchDate.toggle() }) {
                    Text(showingLaunchDate ? "Show Crew" : "Show Launch Date")
                        .frame(width: 150, alignment: .trailing)

Because I added a specific frame width of 150, the text stay aligned to the right side of the nav bar when switching between "Show Crew" and "Show Launch Date".

For now, I think it best not to provide a ternary operator inside the navigation bar that changes between different sized views and just stick with the design guidelines of having minimal complexity.

One final note... it appears that the shifting View happens not only between Long and Short Views, but 2 Views with any difference in width.

For instance, replace the textView function in my example above with this and you'll see that the text will still move back and forth when toggling...

func textView() -> Text {
        if showingLongTextView {
            return Text("Shorty View")
        } else {
            return Text("Short view")
        }
    }

Best, Zoyd

3      

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.