Custom back button for navigation bar in SwiftUI

In this tutorial, we will see how to create a custom back button and implement a custom action for the navigation bar in SwiftUI.

When we are using an iPhone app, there is a little Back button in the top left corner that takes us to the previous screen. We can customize that back button and also its action but this will be not a straightforward way. Apple makes it a bit tricky if we want to do something special when someone presses that button.

This is because Apple wants to maintain consistent navigation view behavior across its entire app ecosystem.

Custom back button and action in SwiftUI

If we want a custom back button and action, we need to do two things which are mentioned below.

  • Hide the default back button
  • Create own custom back button.

Now, I will explore the above two approaches to achieve this task.

Hide the default back button

First of all, we have to hide the default back button that SwiftUI provides when we navigate to another view. We can achieve this by using the navigationBarBackButtonHidden() modifier and specifying the boolean value true as an argument within this modifier.

Syntex:

.navigationBarBackButtonHidden(true)

Simply, we have to apply the above modifier to the view for which we want to hide the back button.

Example

struct DetailView: View {
    var body: some View {
        Text("Detail View")
            .navigationBarBackButtonHidden(true)
    }
}

In the above program, I have hidden the navigation back button for the DetailView using the .navigationBarBackButtonHidden(true) modifier.

So, when we navigate to the DetailView there will be no back button in that view.

Output: Hide the default back button

As you can see in the above output, the navigation back button has been removed.

Create own custom back button.

After hiding the default back button, We can create our own button to act as the back button. We can do this using the toolbar modifier along with ToolbarItem(placement: .navigationBarLeading) to position the button on the leading edge of the navigation bar.

Within the ToolbarItem I will create a button with a label and a back button symbol using the SF Symbol. I will use HStack to align the button and the symbol horizontally.

Example

struct DetailView: View {
    var body: some View {
        Text("Detail View")
        
            // Hide the default back button in the navigation bar
            .navigationBarBackButtonHidden(true)
        
            // Define custom toolbar items for the navigation bar
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        // Action to be performed when the custom back button is tapped
                        print("Button Pressed")
                    } label: {
                        // Creating a button with an image and text
                        HStack {
                            Image(systemName: "chevron.backward") // Back chevron icon
                            Text("Custom Button") // Text label for the back button
                        }
                    }
                }
            }
    }
}

In the above program, I have used toolbar modifier along with the ToolbarItem to customize the navigation bar of our DetailView by adding a custom button with a specific placement and action.

Then, Within the ToolbarItem, I have created a button with the label “Custom Button” and an icon of a back chevron (<).

I have added the ToolbarItem with a placement of .navigationBarLeading, to place the button on the leading side of the navigation bar.

So, when we navigate to the DetailView it will display the text “Detail View” in the center of the screen, and a custom back button (“Custom button“) to the leading side of the navigation bar.

Now, have a look at the complete code below.

import SwiftUI

// The view where we want to navigate
struct DetailView: View {
    var body: some View {
        Text("Detail View")
        
            // Hide the default back button in the navigation bar
            .navigationBarBackButtonHidden(true)
        
            // Define custom toolbar items for the navigation bar
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        // Action to be performed when the custom back button is tapped
                        print("Button Pressed")
                    } label: {
                        // Creating a button with an image and text
                        HStack {
                            Image(systemName: "chevron.backward") // Back chevron icon
                            Text("Custom Button") // Text label for the back button
                        }
                    }
                }
            }
    }
}

// The main view, from where we want to navigate
struct ContentView: View {
    var body: some View {
        NavigationView {
            // Navigate to the DetailView when this link is tapped
            NavigationLink("Detail") {
                DetailView()
            }
            
            // Set the title of the navigation bar in the ContentView
            .navigationTitle("Home")
        }
    }
}

The ContentView is the main view of our SwiftUI app. It contains a NavigationView with a NavigationLink labeled “Detail“, when we tap the link it will navigate us to the DetailView.

Whenever we tap the custom back button (“Custom Button“) of the DetailView, it will print “Button Pressed” each time to the console.

Output:

Custom back button for navigation bar in SwiftUI

Dismiss the View

When we hide the default back button in SwiftUI, it removes the built-in functionality for navigating back.

So, if we add our own custom back button, it won’t automatically handle going back as the default one does. Also, swiping from the leading edge to go back won’t work anymore.

Here, we can use either the DismissAction for iOS 15 or PresentationMode for iOS 13-14, to dismiss the view.

iOS 15: Using DismissAction

In iOS 15 and later versions, we can utilize the DismissAction environment value to dismiss the view.

Example

struct DetailView: View {
    
    // Create an environment variable to access the dismiss action
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        Text("Detail View")

            .navigationBarBackButtonHidden(true)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        
                        // Call the dismiss action to dismiss the current view
                        dismiss()
                        
                    } label: {
                        HStack {
                            Image(systemName: "chevron.backward")
                            Text("Custom Button")
                        }
                    }
                }
            }
    }
}

In the above program, I have declared the @Environment(\.dismiss) to access the dismissal functionality, and then I have called the dismiss() within the button action to dismiss the view.

Output:

Dismiss the View using DismissAction

iOS 13-14: Using PresentationMode

For versions earlier than iOS 15, we can use PresentationMode environment value.

Example

struct DetailView: View {
    
    // Access the presentation mode environment variable to control navigation
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        Text("Detail View")

            .navigationBarBackButtonHidden(true)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        
                        // Custom button action to dismiss the current view
                        presentationMode.wrappedValue.dismiss()
                        
                    } label: {
                        HStack {
                            Image(systemName: "chevron.backward")
                            Text("Custom Button")
                        }
                    }
                }
            }
    }
}

In the above program, I have declared @Environment(\.presentationMode) and called presentationMode.wrappedValue.dismiss() within the button action to dismiss the view.

Output:

Dismiss the View using PresentationMode

Leave a Reply

Your email address will not be published. Required fields are marked *