Presenting more than one Sheet in SwiftUI can sometimes be tricky, as it turns out that with the help of an enum and a bit of code organization technique, we can manage multiple sheet launch in SwiftUI easily.
In this article, we will work on a solution to present multiple sheets from a single view. Here is our final product in action:
Let’s start with a data structure to store pages we want to launch in a sheet.
enum Page: Identifiable {
case home, profile, settings
var id: Int {
hashValue
}
}
Next, we will create three views. One for each home, profile and settings. These views will also take a Binding parameter so we can pass data between views.
Home
struct DTHomeView: View {
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Home")
.font(.largeTitle)
Image(systemName: "house")
.font(.largeTitle)
.foregroundColor(.orange)
Button("Increase") {
passedValue += 10
}
}
.navigationTitle("DevTechie.com")
}
}
}
Profile
struct DTProfileView: View {
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Profile")
.font(.largeTitle)
Image(systemName: "person.circle")
.font(.largeTitle)
.foregroundColor(.mint)
Button("Increase") {
passedValue += 10
}
}
.navigationTitle("DevTechie.com")
}
}
}
Settings
struct DTSettingsView: View {
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Settings")
.font(.largeTitle)
Image(systemName: "gear")
.font(.largeTitle)
.foregroundColor(.indigo)
Button("Increase") {
passedValue += 10
}
}
.navigationTitle("DevTechie.com")
}
}
}
For our main view, we will create two state variables. One to pass a value to other views, other is to hold the currentPage. We will have three buttons, each will set currentPage variable’s value to the view it wants to launch.
Now, we will add code to launch sheet. We will pass currentPage as item parameter for sheet and apply a switch statement which will determine which view to display based on the value in currentPage.
.sheet(item: $currentPage) { item in
switch item {
case .home:
DTHomeView(passedValue: $valueToPass)case .profile:
DTProfileView(passedValue: $valueToPass)case .settings:
DTSettingsView(passedValue: $valueToPass)
}
}
But now, we can’t dismiss the sheet. Well there is a simple solution to that too. We will take help from Environment property wrapper.
We use the Environment property wrapper to read a value stored in a view’s environment.
Let’s add Environment property wrapper at the top of our pages, as shown below:
struct DTHomeView: View {
@Environment(\.dismiss) var dismiss
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Home")
.font(.largeTitle)
Image(systemName: "house")
.font(.largeTitle)
.foregroundColor(.orange)
Button("Increase") {
passedValue += 10
}
}
.navigationTitle("DevTechie.com")
}
}
}
Next, add a button to dismiss current view.
struct DTHomeView: View {
@Environment(\.dismiss) var dismiss
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Home")
.font(.largeTitle)
Image(systemName: "house")
.font(.largeTitle)
.foregroundColor(.orange)
Button("Increase") {
passedValue += 10
}
Button("Dismiss") {
dismiss()
}
.buttonStyle(.bordered)
}
.navigationTitle("DevTechie.com")
}
}
}
Build and run:
Now, we are launching full screen sheet, modifying the state and dismissing view without any issues.
Let’s make this change in all views, build and run for one more time.
Complete code below:
struct ContentView: View {
@State var currentPage: Page?
@State var valueToPass = 10
var body: some View {
VStack {
Text("Current Value: \(valueToPass)")
Button {
currentPage = .home
} label: {
Label("Home", systemImage: "house.fill")
.frame(width: 200)
}
.buttonStyle(.borderedProminent)
Button {
currentPage = .profile
} label: {
Label("Profile", systemImage: "person.circle.fill")
.frame(width: 200)
}
.buttonStyle(.borderedProminent)
Button {
currentPage = .settings
} label: {
Label("Settings", systemImage: "gear.circle.fill")
.frame(width: 200)
}
.buttonStyle(.borderedProminent)
}
.fullScreenCover(item: $currentPage) { item in
switch item {
case .home:
DTHomeView(passedValue: $valueToPass)
case .profile:
DTProfileView(passedValue: $valueToPass)
case .settings:
DTSettingsView(passedValue: $valueToPass)
}
}
}
}
struct DTHomeView: View {
@Environment(\.dismiss) var dismiss
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Home")
.font(.largeTitle)
Image(systemName: "house")
.font(.largeTitle)
.foregroundColor(.orange)
Button("Increase") {
passedValue += 10
}
Button("Dismiss") {
dismiss()
}
.buttonStyle(.bordered)
}
.navigationTitle("DevTechie.com")
}
}
}
struct DTProfileView: View {
@Environment(\.dismiss) var dismiss
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Profile")
.font(.largeTitle)
Image(systemName: "person.circle")
.font(.largeTitle)
.foregroundColor(.mint)
Button("Increase") {
passedValue += 10
}
Button("Dismiss") {
dismiss()
}
.buttonStyle(.bordered)
}
.navigationTitle("DevTechie.com")
}
}
}
struct DTSettingsView: View {
@Environment(\.dismiss) var dismiss
@Binding var passedValue: Int
var body: some View {
NavigationStack {
VStack {
Text("Settings")
.font(.largeTitle)
Image(systemName: "gear")
.font(.largeTitle)
.foregroundColor(.indigo)
Button("Increase") {
passedValue += 10
}
Button("Dismiss") {
dismiss()
}
.buttonStyle(.bordered)
}
.navigationTitle("DevTechie.com")
}
}
}
With that we have reached the end of this article. Thank you once again for reading. Don’t forget to follow 😍. Also subscribe our newsletter at https://www.devtechie.com