ios – SwiftUI: Maintain state of bottom sheet when another sheet is opened


I have a very complex app and I have created a sample app to explain my problem.

My app has a bottom sheet (BottomSheetView) which has navigation destinations to drill into other views within the sheet. There is also a button (info button) within the app which when clicked opens another sheet.

Let’s say I tap on a button within BottomSheetView which opens BottomSheetSubView as navigation destination. Then tap on the info button on the app which opens another sheet(InfoView and InfoSubView) over BottomSheetView. When I cancel out of this info sheet view, I notice that bottom sheet’s state changes i.e. bottom sheet shows BottomSheetView instead of BottomSheetSubView.

I want to continue showing BottomSheetSubView since that was the view before I opened info view. How can I keep the state of bottom sheet the same?

Sharing my code below. You probably will have to run it to understand the problem.

import SwiftUI
import Foundation

class MainViewModel: ObservableObject {
    @Published var showBottomPanel = false
    @Published var selectedDetent: PresentationDetent = .fraction(0.25)
}

struct ContentView: View {
    @StateObject var mainViewModel: MainViewModel = MainViewModel()
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("This is the main view").padding(.top, 100.0)
                Spacer()
            }
            .onAppear {
                mainViewModel.showBottomPanel = true
            }
            .overlay(alignment: .bottomTrailing) {
                self.infoView
            }
            .sheet(isPresented: $mainViewModel.showBottomPanel) {
                ZStack {
                    Color.gray.opacity(0.5)
                    BottomSheetView().padding(.top, 15.0).environmentObject(mainViewModel)
                }
            }
        }
    }
    
    private var infoView: some View {
        InfoView().padding(.bottom, 210.0).padding(.trailing, 10.0)
    }
}

// MARK: View's related to bottom sheet.

struct BottomSheetView: View {
    @EnvironmentObject var mainViewModel: MainViewModel
    @State var showSubView = false
    var body: some View {
        NavigationStack {
            ZStack {
                Color.gray.opacity(0.5).edgesIgnoringSafeArea(.all)
                VStack {
                    Button { self.showSubView = true } label: {
                        Text("Click Me")
                    }
                }
                .navigationDestination(isPresented: $showSubView) {
                    BottomSheetSubView()
                }
            }
        }
        .presentationDetents([.fraction(0.25), .large], selection: $mainViewModel.selectedDetent)
        .interactiveDismissDisabled(true)
        .presentationBackgroundInteraction(.enabled)
    }
}

struct BottomSheetSubView: View {
    var body: some View {
        VStack {
            Text("This is a sub view and should stay active.")
        }
    }
}

// MARK: View's related to info view/info button

struct InfoView: View {
    @State var showInfoSubView = false
    
    var body: some View {
        VStack(spacing: 0) {
            Button {
                showInfoSubView = true
            } label: {
                Image(systemName: "info.circle.fill").resizable().foregroundColor(.blue).frame(width: 30, height: 30)
            }
        }
        .sheet(isPresented: $showInfoSubView) {
            InfoSubView()
        }
    }
}

struct InfoSubView: View {
    @Environment(\.presentationMode) private var presentationMode
    
    var body: some View {
        NavigationStack {
            VStack {
                Text("This is info sub view... tap on cancel button")
            }
            .presentationDetents([.large])
            .interactiveDismissDisabled(true)
            .presentationBackgroundInteraction(.enabled)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button {
                        presentationMode.wrappedValue.dismiss()
                    } label: {
                        Text("Cancel").font(.body)
                    }
                }
            }
        }
    }
}

Steps to produce the problem:

  • Run the code above.
  • Tap on “Click Me” on the bottom panel which will navigate it to subview which says – “This is a sub view and should stay active”.
  • Now don’t do anything with bottom panel, but tap on info button to open info view.
  • Once info view is opened, tap ‘Cancel’ on top left of this panel to close the info sheet.
  • Notice the bottom panel goes back to “Click Me” view instead of “This is a sub view and should stay active” view.

I wish to keep “This is a sub view and should stay active” active in the bottom panel once info view is closed. How can I achieve this?

Latest articles

spot_imgspot_img

Related articles

Leave a reply

Please enter your comment!
Please enter your name here

spot_imgspot_img