SwiftUI Sheets Demystified (Episode I°)
In this post, we learn how to use sheets in SwiftUI. We will cover:
- Open and close a not full-screen sheet
- Open and close a full-screen sheet
In this example, i use this image unplash
Creating a Non-Full-Screen Sheet
Begin by creating the project and adding the image to the assets. I’ve renamed it susan.
struct ContentView: View {
@State var isPresented = false
var body: some View {
NavigationStack {
Text("Sheet")
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("+") {
isPresented = true
}.font(.largeTitle)
}
}
}
.sheet(isPresented: $isPresented, content: {
SheetUIView()
})
}
}
First, we add a ‘plus’ button to the toolbar. When this button is tapped, the variable isPresented is set to true. Once isPresented becomes true, the sheet is presented, displaying the SheetUIView.
Define the SheetUIView:
struct SheetUIView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
ScrollView {
VStack {
Image("susan")
.resizable()
.aspectRatio(contentMode: .fit)
Text("The sheet")
}
}.overlay(
HStack {
Spacer()
VStack {
Button(action: {
dismiss()
}, label: {
Image(systemName: "chevron.down.circle.fill")
.font(.largeTitle)
.foregroundColor(.white)
})
.padding(.trailing, 20)
.padding(.top, 10)
Spacer()
}
}
)
}
}
Using the Environment‘s dismiss, we call DismissAction on the current view. The body of the view contains an image at the top and some simple text. Although sheets are typically closed with a top-down swipe action, this might not always be intuitive. Therefore, we add a close button for clarity. Specifically, we place a button on the top-right corner with an action that calls dismiss(), thereby closing the sheet.
Creating a Full-Screen Sheet:
First, let’s start with a small modification in the ContentView.
struct ContentView: View {
@State var isPresented = false
var body: some View {
NavigationStack {
Text("Sheet")
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("+") {
isPresented = true
}.font(.largeTitle)
}
}
}
.fullScreenCover(isPresented: $isPresented){
SheetUIView()
}
}
}
We replace sheet with fullScreenCover for this implementation.
In the SheetUIView, we ignore the safe area to achieve a visually appealing effect:
The code:
struct SheetUIView: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
ScrollView {
VStack {
Image("susan")
.resizable()
.aspectRatio(contentMode: .fit)
Text("The sheet")
}
}.overlay(
HStack {
Spacer()
VStack {
Button(action: {
dismiss()
}, label: {
Image(systemName: "chevron.down.circle.fill")
.font(.largeTitle)
.foregroundColor(.white)
})
.padding(.trailing, 20)
.padding(.top, 10)
Spacer()
}
}
).edgesIgnoringSafeArea(.all)
}
}
A tip for both scenarios mentioned above: If you’re not using an image but rather a VStack within a ScrollView, it’s advisable to define the VStack with these dimensions:
ScrollView {
VStack {
Spacer(minLength: 100)
Text("Hello")
}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
......
This is necessary to prevent the close button, defined in the overlay, from following the text in the VStack and ensure it stays at the top right border.
That’s all for this post. In the next one episode-ii, we will explore additional scenarios for using sheets.
Note: English is not my native language, so I’m sorry for some errors. I appreciate if your correct me.
Top comments (0)