I have a TabView
that presents a sheet after tapping on the [+] (2nd) tabItem
. At the same time, the ContentView
is also switching the TabView
's tab selection, so when I dismiss the sheet that is presented, the selected tab is a blank one without any content. Not an ideal user experience.
My question:
I am wondering how I can simply disable that specific tabItem
so it doesn't "behave like a tab" and simply just present's the sheet while maintaining the previous tab selection prior to tapping the [+] item. Is this possible with SwiftUI or should I got about this another way to achieve this effect?
Image of my tab bar:
Here's the code for my ContentView
where my TabView
is:
struct SheetPresenter<Content>: View where Content: View {
@EnvironmentObject var appState: AppState
@Binding var isPresenting: Bool
var content: Content
var body: some View {
Text("")
.sheet(isPresented: self.$isPresenting, onDismiss: {
// change back to previous tab selection
print("New listing sheet was dismissed")
}, content: { self.content})
.onAppear {
DispatchQueue.main.async {
self.isPresenting = true
print("New listing sheet appeared with previous tab as tab \(self.appState.selectedTab).")
}
}
}
}
struct ContentView: View {
@EnvironmentObject var appState: AppState
@State private var selection = 0
@State var newListingPresented = false
var body: some View {
$appState.selectedTab back to just '$selection'
TabView(selection: $appState.selectedTab){
// Browse
BrowseView()
.tabItem {
Image(systemName: (selection == 0 ? "square.grid.2x2.fill" : "square.grid.2x2")).font(.system(size: 22))
}
.tag(0)
// New Listing
SheetPresenter(isPresenting: $newListingPresented, content: NewListingView(isPresented: self.$newListingPresented))
.tabItem {
Image(systemName: "plus.square").font(.system(size: 22))
}
.tag(1)
// Bag
BagView()
.tabItem {
Image(systemName: (selection == 2 ? "bag.fill" : "bag")).font(.system(size: 22))
}
.tag(2)
// Profile
ProfileView()
.tabItem {
Image(systemName: (selection == 3 ? "person.crop.square.fill" : "person.crop.square")).font(.system(size: 22))
}
.tag(3)
}.edgesIgnoringSafeArea(.top)
}
}
And here's AppState
:
final class AppState: ObservableObject {
@Published var selectedTab: Int = 0
}
You are pretty close to what you want to achieve. You will just need to preserve the previous selected tab index and reset the current selected tab index with that preserved value at the time of the dismissal of the sheet. That means:
.sheet(isPresented: self.$isPresenting, onDismiss: {
// change back to previous tab selection
self.appState.selectedTab = self.appState.previousSelectedTab
}, content: { self.content })
So how do you keep track of the last selected tab index that stays in sync with the selectedTab
property of the AppState
? There may be more ways to do that with the APIs from Combine
framework itself, but the simplest solution that comes to my mind is:
final class AppState: ObservableObject {
// private setter because no other object should be able to modify this
private (set) var previousSelectedTab = -1
@Published var selectedTab: Int = 0 {
didSet {
previousSelectedTab = oldValue
}
}
}
The above solution of may not be the exact thing as disable specific tab item selection but after you dismiss the sheet it will revert back with a soothing animation to the selected tab prior to presenting the sheet. Here is the result.