Skip to content
DeveloperMemos

Using @Binding for Sheet Navigation Control in SwiftUI

SwiftUI, Binding, Programming1 min read

SwiftUI is a powerful framework for building user interfaces across all Apple platforms. One of its key features is the data flow mechanism, which includes property wrappers like @State, @Binding, @EnvironmentObject, and more. In this article, we'll focus on @Binding and how it can be used to control sheet navigation.

What is @Binding?

@Binding is a property wrapper in SwiftUI that establishes a two-way connection between a property that stores data, and a view that displays and changes the data. It allows us to create a mutable state in one view and pass a reference to another view. This way, when the state changes in the second view, it also changes in the first view.

Using @Binding for Sheet Navigation Control

Let's consider the code below. This view is presented as a sheet and has a @Binding variable showPurchaseModal:

1import SwiftUI
2
3struct OnboardingView: View {
4 @Binding var showPurchaseModal: Bool
5
6 var body: some View {
7 VStack {
8 Spacer()
9 Text("Welcome to the App!")
10 Spacer()
11 Button {
12 showPurchaseModal = true
13 } label: {
14 Text("Continue")
15 }
16 }
17 .onDisappear {
18 showPurchaseModal = true
19 }
20 }
21}

In this example, when the "Continue" button is pressed or the OnboardingView disappears, showPurchaseModal is set to true. This change is reflected in the parent view that owns the actual state.

Here's how the parent view might look:

1struct ContentView: View {
2 @State private var showOnboarding = true
3 @State private var showPurchaseModal = false
4
5 var body: some View {
6 VStack {
7 Button {
8 showOnboarding = true
9 } label: {
10 Text("Show Onboarding")
11 }
12 }
13 .sheet(isPresented: $showOnboarding) {
14 OnboardingView(showPurchaseModal: $showPurchaseModal)
15 }
16 .sheet(isPresented: $showPurchaseModal) {
17 Text("Purchase Modal")
18 }
19 }
20}

In ContentView, showOnboarding and showPurchaseModal are @State variables. When showPurchaseModal changes in OnboardingView, it also changes in ContentView due to the @Binding. This triggers a re-render of ContentView, presenting the "Purchase Modal" sheet - after OnboardingView has disappeared(it doesn't matter if the user dismissed with a button or uses the gesture to hide the sheet).

Why would you want to do this?

If you aren't using the new NavigationStack for routing in your app it's kind of difficult to replace one screen with another screen(if your app is still relying just on NavigationLink for example). I personally wanted to show an onboarding screen and then show the user a different screen after that but also dismiss the onboarding screen. I already had the screen that I wanted to show wired up as a sheet in the previous screen. I did do some experimenting with trying to use dismiss but couldn't quite get the functionality I wanted and that's why I settled on the above solution and just past a @Binding that could be triggered in OnboardingView instead.