2
votes

Let's say I have a view in a NavigationView, with leading and trailing buttons. Because I can present two different sheets from this view, I use an enum like that:

enum AccountActiveSheet {
   case about, settings
}

This main view has two @State properties in the view to trigger the sheet presentation.

@State private var isSheetPresented: Bool = false
@State private var activeSheet: AccountActiveSheet = .about

Each button can trigger a sheet.

var aboutButton: some View {
    Button(action: {
        self.activeSheet = .about
        self.isSheetPresented.toggle()
    }, label: {
        Image(systemName: "info.circle.fill")
    })
}

At the end of the navigation view, I select the correct sheet to present like that

.sheet(isPresented: $isSheetPresented) {
   self.activeSheet.sheet
}

It's working fine and presenting the correct view in a modal.

But I also have multiple subviews in this main view that can present sheets when tapping on a button. In this case, the sheet managed by the subview (i.e. a @State variable to track the $isSheetPresented + content for the sheet provided by the subview) is not presented. Nothing happens. It seems the superview sheet modifier is preventing the subview to present the sheet.

Is it possible to have both a superview and a subview manage sheet presentation?

enum AccountActiveSheet {
    case about, settings

    @ViewBuilder
    var sheet: some View {
        switch self {
        case .about: Text("About")
        case .settings: Text("Settings")
        }
    }
}

struct AccountView: View {
    @State private var isSheetPresented: Bool = false
    @State private var activeSheet: AccountActiveSheet = .about
    
    var aboutButton: some View {
        Button(action: {
            self.activeSheet = .about
            self.isSheetPresented.toggle()
        }, label: {
            Image(systemName: "info.circle.fill")
        })
    }
    
    var settingsButton: some View {
        Button(action: {
            self.activeSheet = .settings
            self.isSheetPresented.toggle()
        }, label: {
            Image(systemName: "gearshape.fill")
        })
    }

    var body: some View {
        NavigationView {
            VStack {
                Subview()
            }
            .navigationBarTitle("Account", displayMode: .inline)
            .navigationBarItems(leading: aboutButton, trailing: settingsButton)
            .sheet(isPresented: $isSheetPresented) {
                self.activeSheet.sheet
            }
        }
    }
}

struct Subview: View {
    @State var isSheetPresented: Bool = false

    var body: some View {
        Button("Present sheet") {
            self.isSheetPresented.toggle()
        }
        .sheet(isPresented: $isSheetPresented) {
            Text("Subview")
        }
    }
}
1
The provided code is not enough to reproduce your case. Would you provided complete reproducible example?Asperi
@Asperi I updated my answer to include full working example. Let me know!alpennec
I noticed this happens with iOS 14.4 only!FarouK

1 Answers

3
votes

Move root view sheet out of NavigationView (tested with Xcode 12.1 / iOS 14.1)

var body: some View {
    NavigationView {
        VStack {
            Subview()
        }
        .navigationBarTitle("Account", displayMode: .inline)
        .navigationBarItems(leading: aboutButton, trailing: settingsButton)
    }
    .sheet(isPresented: $isSheetPresented) {   // << here !!
        self.activeSheet.sheet
    }
}