Creating Multiple Sheets in SwiftUI
Learn how to display multiple dialogs and modals in SwiftUI using boolean bindings or Identifiable items
To display modals or dialogs in SwiftUI, you can use the sheet feature in two ways. You can use a boolean binding as follows:
func sheet<Content>(
isPresented: Binding<Bool>,
onDismiss: (() -> Void)? = nil,
content: @escaping () -> Content
) -> some View where Content : View
Or you can use an Identifiable item binding like this:
func sheet<Item, Content>(
item: Binding<Item?>,
onDismiss: (() -> Void)? = nil,
content: @escaping (Item) -> Content
) -> some View wh
You can use both approaches as needed, and even combine them.
#Displaying Multiple Modals/Dialogs with Boolean Binding
To display multiple modals/dialogs using boolean bindings, the process is straightforward. Simply add multiple state variables of type boolean and attach the .sheet modifier for each. Here's an example:
import SwiftUI
struct ContentView: View {
@State private var mdl1: Bool = false
@State private var mdl2: Bool = false
var body: some View {
VStack {
HStack {
Button("Modal 1") {
mdl1 = true
}
Button("Modal 2") {
mdl2 = true
}
}
}
.padding()
.sheet(isPresented: $mdl1) {
Text("Modal 1")
}
.sheet(isPresented: $mdl2) {
Text("Modal 2")
}
}
}
This method is quite simple but is suitable for showcasing a limited number of modals/dialogs, typically less than three, as the number of variables and sheet modifiers increases with each modal/dialog.
#Displaying Multiple Modals/Dialogs with Identifiable Binding
Another alternative for displaying multiple modals/dialogs is by using an Identifiable item binding, as demonstrated below:
struct ContentView: View {
enum ActiveSheet: Identifiable {
case modal3, modal4, modal5
var id: Int {
hashValue
}
}
@State private var mdl: ActiveSheet?
var body: some View {
VStack {
HStack {
Button("Modal 3") {
mdl = .modal3
}
Button("Modal 4") {
mdl = .modal4
}
Button("Modal 5") {
mdl = .modal5
}
}
}
.padding()
.sheet(item: $mdl, content: { item in
switch item {
case .modal3: Text("Modal 3")
case .modal4: Text("Modal 4")
case .modal5: Text("Modal 5")
}
})
}
}
To replace the role of multiple boolean state variables, we create an enum that conforms to the Identifiable protocol. To meet the requirements of the Identifiable protocol, we derive the ID from the hashValue. We then create a single state variable, in this case, mdl, to store the active modal.
enum ActiveSheet: Identifiable {
case modal3, modal4, modal5
var id: Int {
hashValue
}
}
@State private var mdl: ActiveSheet?
This approach simplifies the code significantly, as you only need one .sheet modifier, and the content is displayed based on the active type.
.sheet(item: $mdl, content: { item in
switch item {
case .modal3: Text("Modal 3")
case .modal4: Text("Modal 4")
case .modal5: Text("Modal 5")
}
})
One crucial thing to note here is that all cases must be exhaustive. That means you must list all possible cases. If you don't, you'll encounter a Swift error stating that it must be exhaustive.
The advantage of using Identifiable binding is that the code becomes simpler, and you don't need multiple .sheet modifiers. This method is particularly suitable when you want to display numerous modals/dialogs, typically more than three.
Good luck, and hopefully this little bit is useful.
Reference:
developer.apple.com