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 unsplash

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)
                            .foregroundStyle(.white)
                    })
                        .padding(.trailing, 20)
                        .padding(.top, 10)
                    Spacer()
                }
            }
        ).ignoresSafeArea(edges: .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, sorry for any errors. I use ChatGPT only to generate the banner of the post, the content is human.

share this post with friends

Picture of Nicola De filippo

Nicola De filippo

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve

2 comments

Leave a comment

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

Who I am

I'm a software engineer who adds to the passion for technologies the wisdom and the experience without losing the wonder for the world. I love to create new projects and to help people and teams to improve.

Follow Me Here

Get The Latest Updates

Periodically receive my super contents on coding and programming

join the family;)

Recent Posts