Presenting Sheets in SwiftUI

Posted in SwiftUI

May 27th, 2021

⏱ Reading Time: 5 mins

A sheet in iOS is a system provided view that appears modally on top of any other currently displayed view. A sheet is by default empty, and it’s our job as developers to provide it with custom views and content. It constitutes a good solution in order to show additional information to users, ask for their input, or let them perform an operation without leaving entirely what they have been currently doing. Presenting sheets in SwiftUI is a fast and a no-brainer task, so read on to find out the few how-to guidelines that govern it.

Presenting a sheet

A sheet in SwiftUI is available as a view modifier, which is usually applied to the outermost view object. Whether the sheet is presented or not is something we usually control with a boolean @State property declared locally in the view’s structure. Initially we set it to false, indicating that way that the sheet must be hidden. In order to make the sheet appear, we change its value to true when necessary or after a user’s interaction.

A sheet view modifier expects two arguments; the first is the binding value of the property that controls its presented state. The second is a closure, where we provide the view that will be displayed as the sheet’s content.

To get a taste of everything just described, consider the following simple example where a button’s tap triggers the presentation of a sheet:

See that the default value of showSheet is false, and we change that in the button’s action closure. Also see how we integrate the sheet; it’s a view modifier applied to the button in this example, as that’s the only view here. The $showSheet binding value determines the sheet’s presented state, and the content closure is the place that contains whatever must be displayed to the user. In this case is just a simple text.

The above simple implementation results to this:

Modal sheet is presented

Dismissing a sheet

There are three ways to dismiss a sheet. The first and simplest one is provided by iOS, and requires no effort at all from the developer. All we have to do is to drag it towards bottom, and the sheet will go away.

The second approach is mostly convenient when we implement sheet’s content right in its content closure. There, we simply change the showSheet value to false, and the sheet will be dismissed.

In the following example, there is a button along with the text in the sheet. By tapping on it, the showSheet property becomes false:

The third method in order to dismiss a sheet becomes handy when the sheet content is implemented in a different view structure. Suppose that we have the following view:

To use it, we just need to create a SheetContentView instance in the sheet’s content closure:

Right now there is no way to dismiss the sheet, other than dragging it downwards. To make it disappear when tapping on the dismiss button above, it’s necessary to access the presentationMode value from SwiftUI’s environment using the @Environment property wrapper:

Then, we can dismiss the sheet by calling the dismiss() method through the wrappedValue of the presentationMode property:

Alternatively, we can dismiss the sheet by passing the binding value of the showSheet property that controls the presented state to the sheet’s content view. To do that, we must declare a property marked with the @Binding property wrapper in that view first:

Next, set false to showSheet in the button that dismisses the sheet:

Lastly, initialize a SheetContentView instance passing the binding value of the showSheet state property:

The last approach demonstrated right above is the one that requires the most effort, so unless you have a specific reason to use it, you’d better stick to the presentationMode property from the SwiftUI views’ environment. It’s faster and easier.

Modal sheet is dismissed

Sheet with full screen cover

Most of the times, the default sheet’s behavior that allows to dismiss it by dragging downwards is a convenient feature. However, there might be cases where it’s more suitable to avoid that, and dismiss the sheet programmatically after some user’s input or interaction.

In order to manage that, it’s needed to present the sheet so it covers the full screen. Thankfully, that’s pretty easy to do; instead of using the sheet(isPresented:content:) view modifier, simply use the fullScreenCover(isPresented:content:) modifier:

One sheet for many occasions

Sometimes there might be occasions where you will want to present a sheet for more than one purposes, and obviously with different content. In such circumstances, declaring multiple sheet view modifiers in order to cover all cases is not recommended; you will also need an equal number of state properties to manage the presented state of the sheets.

The best solution is to have one sheet view modifier only, and present the proper content conditionally in the sheet’s content closure. That way you will have one state property to control the sheets appearance, and a more elegant solution as you are going to see right next.

Regarding the conditional presentation of sheet content, you can use either boolean properties, or follow another approach, which is my favorite one; to declare a custom enumeration with cases matching to each occasion that sheet should be presented for.

The following example demonstrates how to do that:

SheetContent is a custom type with cases representing the content that the sheet should contain, and sheetContent property is of that type. Depending on which one of the three buttons is tapped, sheetContent gets the proper value that indicates the view that should be displayed when the sheet will appear.

In the sheet’s content closure, there is a switch statement that checks the value of the sheetContent property, and depending on the case, it initializes the proper view.

Performing actions on sheet’s dismiss event

In certain cases you may need to perform additional actions right after the sheet has been dismissed. To manage that, it’s necessary to use another initializer for the sheet view modifier, called sheet(isPresented:onDismiss:content:).

The onDismiss parameter is a closure that gets called right upon the sheet’s dismissal. That’s the place to add any tasks you want to perform once that event has occurred.

The following example prints a message right when the sheet is dismissed:

Summary

As you can see, presenting sheets in SwiftUI is not a difficult task. In this post I shared with you pretty much everything you need to know about them; how to present, dismiss, and provide content to a sheet, as well as a few useful tips that will make things easier for you. Modal sheets can be proved a handy tool when building apps, so why not using them when that seems appropriate? Thank you for reading!

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.