Environment Key
and EnvironmentValues
to achieve the programmatic tab switching in TabView
.Lets start with the empty SwiftUI template from the Xcode and Create new SwiftUI view named AppTabView.swift.Define AppTabView
Here we define our tabs in AppTabView like below.struct AppTabView: View {
enum Tab {
case profile
case bookmarks
case settings
}
@State private var selectedTab: Tab = .profile
var body: some View {
TabView(selection: $selectedTab) {
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(Tab.profile)
BookmarksView()
.tabItem {
Image(systemName: "book")
Text("Bookmarks")
}
.tag(Tab.bookmarks)
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}
.tag(Tab.settings)
}
}
}
.tag
modifier in order to TabView to switch the current tab.If we use @SceneStorage("selectedTab") instead of @State, then we can get some level of state-restoration behaviour.Now below is the contents of the
ProfileView
, BookmarksView
and SettingsView
.struct ProfileView: View {
var body: some View {
NavigationView {
List {
Button("Bookmarks") {
print("Switch to Bookmarks Tab")
}
Button("Settings") {
print("Switch to Settings Tab")
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Profile")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct BookmarksView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: BookmarkDetailView()) {
Text("ICE")
}
Button("Settings") {
print("Switch to Settings Tab")
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Bookmarks")
.navigationBarTitleDisplayMode(.inline)
}
}
}
struct SettingsView: View {
var body: some View {
NavigationView {
List {
Button("Profile") {
print("Switch to Profile View")
}
Button("Bookmarks") {
print("Switch to Bookmarsk View")
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
}
}
}
AppTabView
in our case ). So we can use the EnvironmentKey
and EnvironmentValues
here.So let's start defining our EnviromentKey named CurrentTabKey
like below.struct CurrentTabKey: EnvironmentKey {
static var defaultValue: AppTabView.Tab = .bookmarks
}
EnvironmentKey
we need to extend the EnvironmentValues
like below.extension EnvironmentValues {
var currentTab: AppTabView.Tab {
get { self[CurrentTabKey.self] }
set { self[CurrentTabKey.self] = newValue }
}
}
CurrentTabKey
like below. ...
TabView(selection: $selectedTab) {
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(Tab.profile)
.environment(\.currentTab, selectedTab)
...
ProfileView
we can use the environment property wrapper like below.struct ProfileView: View {
@Environment(\.currentTab) var tab
var body: some View {
NavigationView {
List {
Button("Bookmarks") {
tab = .bookmarks
}
Button("Settings") {
tab = .settings
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Profile")
.navigationBarTitleDisplayMode(.inline)
}
}
}
Cannot assign to property: 'tab' is a get-only propertyTo solve this, we need to recall the two way data communication in SwiftUI. For this SwiftUI has property wrapper called
Binding
.Set CurrentTabKey
So we update our EnvironmentKey struct CurrentTabKey like below.struct CurrentTabKey: EnvironmentKey {
static var defaultValue: Binding<AppTabView.Tab> = .constant(.bookmarks)
}
extension EnvironmentValues {
var currentTab: Binding<AppTabView.Tab> {
get { self[CurrentTabKey.self] }
set { self[CurrentTabKey.self] = newValue }
}
}
Cannot convert value 'selectedTab' of type 'AppTabView.Tab' to expected type 'Binding<AppTabView.Tab>', use wrapper insteadFixing the above compiler error by putting the
Insert $
$
in the environment modifier like below....
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(Tab.profile)
.environment(\.currentTab, $selectedTab)
...
...
Button("Bookmarks") {
tab.wrappedValue = .bookmarks
}
Button("Settings") {
tab.wrappedValue = .settings
}
...
Set BookmarkDetailView
struct BookmarkDetailView: View {
@Environment(\.currentTab) var tab
var body: some View {
List {
Button("Profile") {
tab.wrappedValue = .profile
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Bookmark Detail")
.navigationBarTitleDisplayMode(.inline)
}
}


