NEW BOOK! Swift Gems: 100+ tips to take your Swift code to the next level. Learn more ...NEW BOOK! Swift Gems:100+ advanced Swift tips. Lear more...

Show multiple sheets at once in SwiftUI

To present a modal sheet in SwiftUI we use sheet(isPresented:onDismiss:content:) modifier where the presentation is controlled by a Boolean value or sheet(item:onDismiss:content:) which accepts a binding to an optional item.

We can place as many of these modifiers as we need throughout our view hierarchy when building apps with SwiftUI. But where exactly we place them can affect the functionality in some situations.

In this artictle I would like to discuss how to use sheet() modifier to be able to present multiple sheets at once in iOS. We can see an example of such functionality in Apple Calendar app. When we add a new calendar from Calendars sheet, it shows a new sheet on top of an already presented one.

Screenshot of Apple Calendar app on iPhone showing calendars modal sheet Screenshot of Apple Calendar app on iPhone showing new calendar modal sheet on top of calendars sheet

To achieve a similar behavior in our app, we have to make sure that we place the second sheet() modifier inside the content of the first sheet.

struct ContentView: View {
    @State private var showCalendars = false
    @State private var showNewCalendar = false
    
    var body: some View {
        Button("Calendars") {
            showCalendars = true
        }
        .sheet(isPresented: $showCalendars) {
            Text("Calendars View")
            Button("Add Calendar") {
                showNewCalendar = true
            }
            // This sheet CAN be presented on top of Calendars
            .sheet(isPresented: $showNewCalendar) {
                Text("New Calendar View")
            }
        }
    }
}

If we were to place these modifiers differently, for example like in the code below, we would get a runtime warning: Currently, only presenting a single sheet is supported. The next sheet will be presented when the currently presented sheet gets dismissed.

struct ContentView: View {
    @State private var showCalendars = false
    @State private var showNewCalendar = false
    
    var body: some View {
        Button("Calendars") {
            showCalendars = true
        }
        .sheet(isPresented: $showCalendars) {
            Text("Calendars View")
            Button("Add Calendar") {
                showNewCalendar = true
            }
        }
        // This sheet CANNOT be presented on top of Calendars
        .sheet(isPresented: $showNewCalendar) {
            Text("New Calendar View")
        }
    }
}

In other situations where you don't need to present multiple sheets at the same time, it's perfectly fine to reorganize your modifiers in a way that suits you. Just keep in mind that the view that contains the modifier has to be on screen when the sheet is summoned, otherwise the sheet won't be shown. It can happen if the view is inside an if...else, for example.

Some of the other modal presentations such as popover and full screen cover work in a similar way to sheets and you can show them one of top of the other. But you can only ever present a single alert or confirmation dialog at once.

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Check out our new book!

Swift Gems

100+ tips to take your Swift code to the next level

Swift Gems

100+ tips to take your Swift code to the next level

  • Advanced Swift techniques for experienced developers bypassing basic tutorials
  • Curated, actionable tips ready for immediate integration into any Swift project
  • Strategies to improve code quality, structure, and performance across all platforms
  • Practical Swift insights from years of development, applicable from iOS to server-side Swift