In this post, we will explore the
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)
}
}
}
Here you can see we have used the
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
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)
}
}
}
Now we have the our basic tabs setup, now we need some way to pass the button press action to super view (
So let's start defining our EnviromentKey named
struct CurrentTabKey: EnvironmentKey {
static var defaultValue: AppTabView.Tab = .bookmarks
}
To use above
extension EnvironmentValues {
var currentTab: AppTabView.Tab {
get { self[CurrentTabKey.self] }
set { self[CurrentTabKey.self] = newValue }
}
}
Now we can use this
...
TabView(selection: $selectedTab) {
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(Tab.profile)
.environment(\.currentTab, selectedTab)
...
And in the
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)
}
}
}
Here we can use the environment variable in our button action. But...
Cannot assign to property: 'tab' is a get-only property
To solve this, we need to recall the two way data communication in SwiftUI. For this SwiftUI has property wrapper called
Set CurrentTabKey
So we update our EnvironmentKey struct CurrentTabKey like below.
struct CurrentTabKey: EnvironmentKey {
static var defaultValue: Binding<AppTabView.Tab> = .constant(.bookmarks)
}
And EnvironmentValues like below.
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 instead
Insert $
Fixing the above compiler error by putting the
...
ProfileView()
.tabItem {
Image(systemName: "person")
Text("Profile")
}
.tag(Tab.profile)
.environment(\.currentTab, $selectedTab)
...
And now finaly updating our Views like below.
...
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)
}
}
And here we have it.
We hope you liked it. Thanks for reading.


