Full Sheets In SwiftUI
Sep 30, 2022Full Sheets are used extensively in SwiftUI for presenting views. They are almost similar to sheets. If this were UIKit, you would code something like:
var controller = ViewController()
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: true)
But since this is SwiftUI, we use a view modifier command. For full sheets, there are not a lot of parameters you could play around with. We are going to explore the two commands that you are most likely to encounter in practical development today. So without further ado, let's dive in.
Basic Implementation
All you need to implement a sheet is a state variable and two views (of course that's a necessity, haha).
struct FirstView: View {
//1
@State var presentAnotherView = false
var body: some View {
VStack {
//2
Text("First View")
.bold()
.font(.largeTitle)
//3
Button {
self.presentAnotherView = true
} label: {
Text("Show Another View")
.padding()
.background(Color.orange)
.cornerRadius(10)
.foregroundColor(Color.white)
}
//4
}
.fullScreenCover(isPresented: $presentAnotherView, content: {
AnotherView()
})
}
}
//5
struct AnotherView : View {
var body: some View {
Text("Second View")
.bold()
.font(.largeTitle)
}
}
Result:
Code Explanation:
- This environment object with key
presentationMode
correlates to the boolean binding we created in our presenting view controller, callingdismiss()
on thewrappedValue
causes the binding to turn false, and in turn dismissing the presented View. - Some bold text with large titles which say it's the first view.
- A button, which, on click, turns
presentAnotherView
as true. For the appearance, you can see that the button has an orange background, some corner radius and a foreground color of white over some padding. - This is the key part. The sheet has a binding bool of
presentAnotherView
and the content of the sheet isAnotherView()
. - A dummy second view called
AnotherView()
with some bold text.
Pretty easy to implement, right?
Dismissing the view
Since we learned about presenting, let's take a look at dismissing the view too. It will be a bad UI if one is unable to dismiss it. For that, let's add a cancel button to another view.
struct AnotherView : View {
//1
@Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
//2
HStack {
Button("Cancel") {
//3
self.presentationMode.wrappedValue.dismiss()
}.padding()
Spacer()
}
Spacer()
Text("Second View")
.bold()
.font(.largeTitle)
//4
Spacer()
}
}
}
Result:
In the above code, the key is self.presentationMode.wrappedValue.dismiss()
.
Code Explanation:
- This environment object will help you dismiss the view
- Here, you can see that we have a "Cancel" button in a HStack with a spacer so it is aligned on the left.
- Here is where the actual magic happens. If this were UIKit, you would do
self.dismiss(...)
but in SwiftUI, you simply change it by missing it on the presentation mode wrapped value. Moreover, this wil set thepresentAnotherView
to false in the first view. - You can see the use of spacers. It is so that we can center the text on the screen.
Presenting on change of the object value
In this part of the article, you are going to learn about how to present a full screen cover on change of an object value. Now, you ask, why do we need something like this when we already present sheets with a boolean value. The reason behind it is that apps with complex networking functions need it. What if you have functionality in your app where you download some data and you want to present a view showing more information about it? That is when a sheet with this parameter will be helpful. Apart from that, you can also use this to pass data to the sheet.
So, for implementing this type of sheet, you need an identifiable object. Now you maybe curious, what are identifiable objects? According to apple, these are:
" Classes of types whose instances hold the value of an entity with stable identity "
What, in the practical world, means we need to have an id
property in your object, most probably set to some kind of UUID. UUIDs are unique ids consisting of characters and numbers. Now that you know about that, let's make a basic identifiable struct called Object
which has an id (of course, since it's an identifiable object) and a title.
struct Object : Identifiable {
var id = UUID()
var title : String
}
struct FirstView: View {
@State var presentAnotherView = false
@State var object : Object?
var body: some View {
VStack {
Text("First View")
.bold()
.font(.largeTitle)
Button {
//1
self.object = Object(id: UUID(), title: "Some data from first view")
} label: {
Text("Show Another View")
.padding()
.background(Color.orange)
.cornerRadius(10)
.foregroundColor(Color.white)
}
//2
}
.fullScreenCover(item: $object) { item in
AnotherView(object: item)
}
}
}
Result:
- When the button is clicked, you change the object value to an object id as UUID() and some text for the title. Note here that is the title you are going to present in another view.
- Usage of sheet with different paramenter - one with binding object and another view with an extra parameter. (talked about in more detail later)
- This fullScreenCover is automatically trigged on the change of object.
Note that there if you set the object
value to nil in FirstView
, it will automatically dismiss the full screen cover.
Woohoo, you can see how we're able to pass the data and present a full screen cover on the change of the second screen.
Summarising the whole article, you learned about showing a full sheet by toggling the bool value and showing it by changing the value of an object.