SwiftUI
sheet modifier is used when we want to present a modal view to the user when a provided boolean value becomes true.
In this article, we will explore the sheet
modifier.
Let’s create an example to work with.
struct DevTechieSheetExample: View {
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("Press the button to launch a sheet.")
Button("Tap me") {
}
Spacer()
}
}
}
In order to add a sheet
, we need a boolean
variable which we can pass as binding value to isPresented
parameter of sheet modifier. We will add a State
property called showSheet.
@State private var showSheet = false
Next, we will add sheet
modifier to the Button
and toggle
the showSheet State
variable. As the content trailing closure for the sheet
modifier, we will simply create a Text
view.
struct DevTechieSheetExample: View {
@State private var showSheet = false
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("Press the button to launch a sheet.")
Button("Tap me") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
Text("This is the sheet view.")
.font(.largeTitle)
}
Spacer()
}
}
}
Notice that SwiftUI presents entire view even though we passed just Text view. SwiftUI also adds swipe down to dismiss
the view functionality to the presented sheet.
We can build a separate view and pass that as the content for Sheet view.
struct SheetView: View {
var body: some View {
VStack {
Text("This is a sheet view.")
.font(.title)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("DevTechie")
.font(.largeTitle)
Spacer()
}
}
}
We can programmatically dismiss
the sheet using dismiss
environment
variable.
@Environment(\.dismiss) var dismiss
Let’s add a close button to our sheet view and dismiss
the view by calling dismiss
function from the sheet view.
struct SheetView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
HStack {
Text("This is a sheet view.")
.font(.title)
Spacer()
Button {
dismiss()
} label: {
Image(systemName: "xmark.circle")
.font(.title)
}
}
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("DevTechie")
.font(.largeTitle)
Spacer()
}
}
}
Starting iOS 15, we can disable swipe to dismiss functionality from sheet
with the help of interactiveDismissDisabled
modifier. This is helpful if we are expecting users to take action on the sheet view before they close the sheet down. An example for this could be license or terms agreement view.
Let’s add this modifier to our sheet view.
struct SheetView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
HStack {
Text("This is a sheet view.")
.font(.title)
Spacer()
Button {
dismiss()
} label: {
Image(systemName: "xmark.circle")
.font(.title)
}
}
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("DevTechie")
.font(.largeTitle)
Spacer()
}
.interactiveDismissDisabled()
}
}
interactiveDismissDisabled
modifier takes boolean value as parameter as well so we can enable/disable swipe to dismiss
based on a condition.
We will create a State
variable in our main view. We also need to add a Binding
property to SheetView. SheetView will have a Toggle
so user can agree to the terms and upon the state change, we can enable swipe to dismiss
functionality.
struct DevTechieSheetExample: View {
@State private var showSheet = false
@State private var agreeTerms = false
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("Press the button to launch a sheet.")
Button("Tap me") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet) {
SheetView(agreeTerms: $agreeTerms)
}
Spacer()
}
}
}struct SheetView: View {
@Environment(\.dismiss) var dismiss
@Binding var agreeTerms: Bool
var body: some View {
VStack {
HStack {
Text("This is a sheet view.")
.font(.title)
Spacer()
Button {
dismiss()
} label: {
Image(systemName: "xmark.circle")
.font(.title)
}
}
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("DevTechie")
.font(.largeTitle)
Toggle("Agree to our terms", isOn: $agreeTerms)
.padding()
Spacer()
}
.interactiveDismissDisabled(!agreeTerms)
}
}
Notice that our terms boolean
value is preserved, what if we want to reset the value when user dismisses the sheet. We can use another overload which takes onDismiss
function. onDismiss
function executes when sheet
is dismissed.
struct DevTechieSheetExample: View {
@State private var showSheet = false
@State private var agreeTerms = false
var body: some View {
VStack {
Text("DevTechie")
.font(.largeTitle)
.foregroundColor(.white)
.padding()
.frame(maxWidth: .infinity)
.background(.orange)
Spacer()
Text("Press the button to launch a sheet.")
Button("Tap me") {
showSheet.toggle()
}
.sheet(isPresented: $showSheet, onDismiss: onDismiss) {
SheetView(agreeTerms: $agreeTerms)
}
Spacer()
}
}
func onDismiss() {
agreeTerms = false
}
}