For iPhone users, modal views are a familiar concept. They are commonly used to present forms for the user to fill out. For example, the Calendar app presents a modal view for users to create a new event. Similarly, the apps that come with the iPhone, Contacts and Reminders, also use modal views that expect the user to fill out forms.
What is a Modal View in SwiftUI
From a user experience perspective, a modal view is typically triggered by tapping a button. Again, the modal view transition animation is handled by iOS. When presenting a full-screen modal view, it smoothly slides up from the bottom of the screen.
In previous versions of iOS, modal views covered the entire screen. However, starting with iOS 13, modal views are displayed in a card-like format by default. The modal view no longer covers the entire screen, but instead partially covers the underlying content view. You can still see the top edge of the main/content view. Additionally, with this update, modal views can be dismissed by swiping down from anywhere on the screen. Enabling this gesture requires no additional code, as it is built-in and provided by iOS. However, if you prefer to dismiss a modal view using a button, you can implement that functionality as well.
While modal views are commonly used to present forms, they can also be used to present other types of information.
In SwiftUI, the card presentation style is used to achieve this effect, and is the default presentation style for modal views.
How to present a new view using Sheet
To present a modal view using the card presentation style, the modifier sheet is applied, as shown below:
.sheet(isPresented: $showModal) {
DetailView()
}
A boolean value is accepted to indicate whether the modal view is presented. If isPresented is set to true, the modal view will automatically be presented as a card.
Another way to present the modal view is as follows:
.sheet(item: $itemToDisplay) {
DetailView()
}
Implementing a Modal View Using isPresented
As we discussed earlier, the sheet modifier provides us with two ways to present a modal view. Let’s start by looking at isPresented.
To implement sheet in this way, we need a state variable of type Bool to keep track of the state of the modal view. We can declare it in ContentView as follows:
@State var showDetailView = false
By default it is set to false. The value of this variable will be set to true when the user taps on one of the rows, a change that we will make in the code.
For example, in the following code we add the sheet modifier to a List as follows:
var body: some View {
NavigationStack {
List(articles) { article in
ArticleRow(article: article)
.onTapGesture {
self.showDetailView = true
self.selectedArticle = article
}
.listRowSeparator(.hidden)
}
.listStyle(.plain)
.sheet(isPresented: $showDetailView) {
if let selectedArticle = self.selectedArticle {
ArticleDetailView(article: selectedArticle)
}
}
.navigationTitle("Your Reading")
}
}
}
The presentation of the modal view depends on the value of the showDetailView property. That’s why we specify it in the isPresented parameter. The modifier sheet’s closure describes the layout of the view that will be presented.
The only thing we’re missing is the ability to detect when a user taps on the screen. When we build the UI navigation, we use NavigationLink to handle when the user taps on the screen.
In SwiftUI there is a handler called onTapGesture that can be used to recognize taps on the screen. You can attach this handler to detect user taps.
We’re also going to set the value of the showDetailView variable to true, which is used to trigger the presentation of the modal view.
onTapGesture {
self.showDetailView = true
self.selectedArticle = article
}
So our app would look like this: