Neil
Neil

Reputation: 151

Displaying a Sheet from multiple options in swiftUI

Follow-up question to iOS14 introducing errors with @State bindings

I was displaying a modal sheet from several options, depending on which button was pressed. However, now in iOS14 I get a fatal error caused by the selectedSpeaker/selectedMicrophone/selectedAmp being nil when the sheet displays. I am trying to change to .sheet(item:, content:) but I can't see how to implement the enum and then pass in the appropriate selected object. This is what I was doing previously:

enum ActiveSheet {
    case speakerDetails, micDetails, ampDetails, settings
}
struct FavoritesView: View {
    @State private var selectedSpeaker: Speaker?
    @State private var selectedMicrophone: Microphone?
    @State private var selectedAmp: Amplifier?

    @State private var showingSheet = false
    @State private var activeSheet: ActiveSheet = .settings  

    var body: some View {
    
    List {
        Button(action: {
            self.activeSheet = .settings
            self.showingSheet = true
            }, label: { Text("Settings")})

        Button(action: {
            self.activeSheet = .micDetails
            self.selectedMicrophone = microphones[0]
            self.showingSheet = true
            }, label: { Text("Mic 1")})

        Button(action: {
            self.activeSheet = .micDetails
            self.selectedMicrophone = microphones[1]
            self.showingSheet = true
            }, label: { Text("Mic 2")})

        Button(action: {
            self.activeSheet = .speakerDetails
            self.showingSheet = true
            self.selectedSpeaker = speakers[0]
            }, label: { Text("Speaker 1")})

        Button(action: {
            self.activeSheet = .speakerDetails
            self.showingSheet = true
            self.selectedSpeaker = speakers[1]
            }, label: { Text("Speaker 2")})

    //and so on for activeSheet = .ampDetails in the same way.
        
    }
      .sheet(isPresented: self.$showingSheet) {
                if self.activeSheet == .speakerDetails {
                    SpeakerDetailView(speaker: self.selectedSpeaker!)
                }
                else if self.activeSheet == .micDetails {
                MicDetailView(microphone: self.selectedMicrophone!)
                }
                else if self.activeSheet == .ampDetails {
                AmpDetailView(amp: self.selectedAmp!)
                } else if self.activeSheet == .settings {
                    SettingsView(showSheet: self.$showingSheet))
                }
            }
        }
    }
}

Upvotes: 2

Views: 640

Answers (1)

pawello2222
pawello2222

Reputation: 54486

Here is another approach for your problem which uses sheet(item:content:)

struct ContentView: View {
    @State private var selectedSpeaker: Speaker?
    @State private var selectedMicrophone: Microphone?
    @State private var selectedAmp: Amplifier?
    @State private var showSettingsSheet = false

    var body: some View {
        List {
            settingsSection
            microphonesSection
            // more sections
        }
    }
    
    var settingsSection: some View {
        Button(action: {
            self.showSettingsSheet = true
        }) {
            Text("Settings")
        }
        .sheet(isPresented: self.$showSettingsSheet) {
            SettingsView()
        }
    }
    
    @ViewBuilder
    var microphonesSection: some View {
        Button(action: {
            self.selectedMicrophone = microphones[0]
        }) {
            Text("Mic 1")
        }
        Button(action: {
            self.selectedMicrophone = microphones[1]
        }) {
            Text("Mic 2")
        }
        .sheet(item: self.$selectedMicrophone) {
            MicDetailView(microphone: $0)
        }
    }
}

This way you also don't need enum ActiveSheet.


You can always use @Environment(\.presentationMode) to dismiss a sheet, no need to pass the variable to the sheet (as in SettingsView(showSheet: self.$showingSheet)):

struct SettingsView: View {
    @Environment(\.presentationMode) private var presentationMode

    var body: some View {
        Text("SettingsView")
            .onTapGesture {
                presentationMode.wrappedValue.dismiss()
            }
    }
}

Upvotes: 1

Related Questions