Inconsistent spacing between navigation bar toolbar items in SwiftUI

42 Views Asked by At

I have 2 trailing navigation bar buttons called Draw and Save.

On tapping Draw, I want 2 additional toolbar buttons to be added to the navigation bar.

This is how I am adding my nav bar buttons:

var body: some View {
    ZStack {
        PDFRepresentedView(drawingMode: $pdfViewerViewModel.drawingMode,
                           viewState: $pdfViewerViewModel.pdfViewState,
                           url: pdfViewerViewModel.pdfFileURL)
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                nextPageNavBarItem
            }
            
            ToolbarItem(placement: .navigationBarTrailing) {
                previousPageNavBarItem
            }
            
            ToolbarItem(placement: .navigationBarTrailing) {
                drawNavBarItem
            }
            
            ToolbarItem(placement: .navigationBarTrailing) {
                saveNavBarItem
                    .bold()
            }
            
            ToolbarItem(placement: .navigationBarLeading) {
                cancelNavBarTitle
                    .alert(isPresented: $shouldShowCancelAlert) {
                        Alert(title: Text(pdfViewerViewModel.localizedText(for: .warningAlertTitle)),
                              message: Text(pdfViewerViewModel.localizedText(for: .cancelWarningMessage)),
                              primaryButton: .cancel(Text(pdfViewerViewModel.localizedText(for: .stayButtonTitle))),
                              secondaryButton: .destructive(Text(pdfViewerViewModel.localizedText(for: .leaveButtonTitle))) {
                            pdfViewerViewModel.shouldSaveData = false
                            dismiss()
                        })
                    }
            }
        }
    }
}

These are the buttons:

private var saveNavBarItem: some View {
    return AnyView(Button(action: {
        pdfViewerViewModel.shouldSaveData = false
        dismiss()
    }, label: {
        Text(pdfViewerViewModel.localizedText(for: .saveButtonTitle))
    }))
}

@ViewBuilder
private var drawNavBarItem: some View {
    Button(action: {
        pdfViewerViewModel.drawingMode.toggle()
    }, label: {
        Image(systemName: pdfViewerViewModel.drawingIconName())
    })
}

@ViewBuilder
private var nextPageNavBarItem: some View {
    if pdfViewerViewModel.drawingMode {
        Button(action: {
            pdfViewerViewModel.pdfViewState = .nextPage
        }, label: {
            Image(systemName: pdfViewerViewModel.nextPageIconName())
        })
    }
}

@ViewBuilder
private var previousPageNavBarItem: some View {
    if pdfViewerViewModel.drawingMode {
        Button(action: {
            pdfViewerViewModel.pdfViewState = .previousPage
        }, label: {
            Image(systemName: pdfViewerViewModel.previousPageIconName())
        })
    }
}

So it starts out like this:

Navigation view, Navigation stack bar button items swiftUI

Then when I tap draw, the 2 other buttons get added, however, the spacing as you see is off between the draw and the previous (up arrow) button:

SwiftUI Navigation Toolbar buttons

If those 2 buttons are there from the beginning without any show / hide logic / binding, the spacing seems to be consistent:

Toolbar Navigation bar buttons SwiftUI

How can I achieve standard spacing when adding buttons to the bar button item at run time

1

There are 1 best solutions below

3
Benzy Neez On BEST ANSWER

I'm not able to reproduce this problem, the gaps are consistent when I toggle the flag that controls their visibility. But a workaround might be to show a hidden placeholder in the position where a button will be shown. Something like:

private func buttonPlaceholder(systemName: String) -> some View {
    Button {} label: {
        Image(systemName: systemName)
    }
    .hidden()
    .accessibilityHidden(true)
}
@ViewBuilder
private var nextPageNavBarItem: some View {
    if pdfViewerViewModel.drawingMode {
        Button(action: {
            pdfViewerViewModel.pdfViewState = .nextPage
        }, label: {
            Image(systemName: pdfViewerViewModel.nextPageIconName())
        })
    } else {
        buttonPlaceholder(systemName: pdfViewerViewModel.nextPageIconName())
    }
}