Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple sheet(isPresented:) doesn't work in SwiftUI

People also ask

How do I make multiple sheets in SwiftUI?

In order for us to be able to use multiple sheets, each modifier has to be applied to the button directly. Using two or more consecutive sheet modifiers does not work in SwiftUI, only the last sheet modifier will be applied and can be used.

How do I use sheets in SwiftUI?

To use a sheet, give it something to show (some text, an image, a custom view, etc), add a Boolean that defines whether the detail view should be showing, then attach it to your main view as a modal sheet. Important: If you're targeting iOS 14 or below, you should use @Environment(\.

How do I dismiss a sheet in SwiftUI?

The first option is to tell the view to dismiss itself using its presentation mode environment key. Any view can read its presentation mode using @Environment(\. presentationMode) , and calling wrappedValue. dismiss() on that will cause the view to be dismissed.

How do I create an action sheet in SwiftUI?

To create your action sheet using confirmationDialog() , provide some title text, a binding to determine when the sheet should be shown, and optionally also whether you want the title text to appear – if you don't specify this, SwiftUI will decide for you based on context.


UPD

Starting from Xcode 12.5.0 Beta 3 (3 March 2021) this question makes no sense anymore as it is possible now to have multiple .sheet(isPresented:) or .fullScreenCover(isPresented:) in a row and the code presented in the question will work just fine.

Nevertheless I find this answer still valid as it organizes the sheets very well and makes the code clean and much more readable - you have one source of truth instead of a couple of independent booleans

The actual answer

Best way to do it, which also works for iOS 14:

enum ActiveSheet: Identifiable {
    case first, second
    
    var id: Int {
        hashValue
    }
}

struct YourView: View {
    @State var activeSheet: ActiveSheet?

    var body: some View {
        VStack {
            Button {
                activeSheet = .first
            } label: {
                Text("Activate first sheet")
            }

            Button {
                activeSheet = .second
            } label: {
                Text("Activate second sheet")
            }
        }
        .sheet(item: $activeSheet) { item in
            switch item {
            case .first:
                FirstView()
            case .second:
                SecondView()
            }
        }
    }
}

Read more here: https://developer.apple.com/documentation/swiftui/view/sheet(item:ondismiss:content:)

To hide the sheet just set activeSheet = nil

Bonus: If you want your sheet to be fullscreen, then use the very same code, but instead of .sheet write .fullScreenCover


Please try below code

Update Answer (iOS 14, Xcode 12)

enum ActiveSheet {
   case first, second
   var id: Int {
      hashValue
   }
}

struct ContentView: View {

    @State private var showSheet = false
    @State private var activeSheet: ActiveSheet? = .first

    var body: some View {
    
        NavigationView {
            VStack(spacing: 20) {
                Button("First modal view") {
                    self.showSheet = true
                    self.activeSheet = .first
                }
                Button ("Second modal view") {
                    self.showSheet = true
                    self.activeSheet = .second
                }
            }
            .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
            .sheet(isPresented: $showSheet) {
                if self.activeSheet == .first {
                    Text("First modal view")
                }
                else {
                    Text("Only the second modal view works!")
                }
            }
        }
    }
}

Can also add the sheet to an EmptyView placed in the view's background. This can be done multiple times:

  .background(EmptyView()
        .sheet(isPresented: isPresented, content: content))

You're case can be solved by the following (tested with Xcode 11.2)

var body: some View {

    NavigationView {
        VStack(spacing: 20) {
            Button("First modal view") {
                self.firstIsPresented.toggle()
            }
            .sheet(isPresented: $firstIsPresented) {
                    Text("First modal view")
            }
            Button ("Second modal view") {
                self.secondIsPresented.toggle()
            }
            .sheet(isPresented: $secondIsPresented) {
                    Text("Only the second modal view works!")
            }
        }
        .navigationBarTitle(Text("Multiple modal view problem"), displayMode: .inline)
    }
}

You can accomplish this simply by grouping the button and the .sheet calls together. If you have one leading and one trailing it is that simple. However, if you have multiple navigationbaritems in either the leading or trailing you need to wrap them in an HStack and also wrap each button with its sheet call in a VStack.

Here's an example of two trailing buttons:

            trailing:
            HStack {
                VStack {
                    Button(
                        action: {
                            self.showOne.toggle()
                    }
                    ) {
                        Image(systemName: "camera")
                    }
                    .sheet(isPresented: self.$showOne) {
                        OneView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//showOne vstack

                VStack {
                    Button(
                        action: {
                            self.showTwo.toggle()
                    }
                    ) {
                        Image(systemName: "film")
                    }
                    .sheet(isPresented: self.$showTwo) {
                        TwoView().environment(\.managedObjectContext, self.managedObjectContext)
                    }
                }//show two vstack
            }//nav bar button hstack