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?