Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

SwiftUI TabView: Explained with Code Examples

The SwiftUI TabView allows you to create a tabbed view and is a great container for several of your main views. Each tab displays another main view, allowing your user to quickly switch between the most important sections of your app.

Developers often use a SwiftUI Tab view as the app’s core container. It is one of the first views an end user sees, and tab views are frequently used to switch between core sections. Apple also uses this common navigation pattern, like in the official iOS App Store application.

Creating a basic SwiftUI TabView

Let’s start with a simple example of a SwiftUI TabView with two tabs. Each tab shows a different SwiftUI view:

struct ContentView: View {
    var body: some View {
        TabView {
            Tab("Home", systemImage: "house") {
                HomeView()
            }
            Tab("Settings", systemImage: "gear") {
                SettingsView()
            }
        }
    }
}

In this example, the TabView holds two tabs: “Home” and “Settings”. Each tab item takes a title and a system image that references an SF Symbol (learn more about SF Symbols here). SwiftUI takes care of the navigation and visual styling for you.

A basic example of a SwiftUI TabView showing two tab views.
A basic example of a SwiftUI TabView showing two tab views.

Showing badges on a tab view

You can show a badge on a tab item to indicate to a user that a tab view contains important information. For example, you could show an exclamation mark if something is wrong or a count to represent the number of unread messages:

struct ContentView: View {
    var body: some View {
        TabView {
            Tab("Home", systemImage: "house") {
                HomeView()
            }
            Tab("Messages", systemImage: "message") {
                MessagesView()
            }
                // You can use integers:
                .badge(2)
            Tab("Settings", systemImage: "gear") {
                SettingsView()
            }
                // Or you can use a `String`:
                .badge("!")
        }
    }
}

This results in the following tabbed view in SwiftUI:

A tabbed view in SwiftUI with badges.
A tabbed view in SwiftUI with badges.

As you can see, you can both use integers and strings as a value for a badge.

Get the code examples for this article

Join 20,022 developers that stay up to date using my weekly newsletter and get access to code examples for all my articles:

Programmatically selecting a tab view in SwiftUI

It’s common to programmatically switch between SwiftUI tabbed views, which can be achieved by using the value parameter in combination with a selection state:

struct ContentView: View {
    
    @State private var selection: Int = 0
    
    var body: some View {
        TabView(selection: $selection) {
            Tab("Home", systemImage: "house", value: 0) {
                Button("Go to settings") {
                    // Change the selection on button tap to match the value of Settings:
                    selection = 1
                }
            }
            Tab("Settings", systemImage: "gear", value: 1) {
                SettingsView()
            }
        }
    }
}

In this example, we’ve added a new state property selection, and used it in the SwiftUI TabView initializer. We assigned a value to each tab and used it accordingly when the user tapped the button.

You can change the default value of selection to change the default selected tab, if you will.

Allowing users to change the order of tab views

When you’re using the sidebarAdaptable style, you can allow your users to drag tabs from the sidebar to the tab bar, hide tabs, and rearrange tabs within the sidebar. This option can be convenient if your app has many important pages and a user preference could apply.

In the following example, we allow users to modify the messages and settings page. In this case, the user can’t adjust the home page which makes it always appear as the first item of the SwiftUI TabView:

struct CustomizationTabView: View {
    
    @AppStorage("tab-view-customization")
    private var customization: TabViewCustomization
    
    var body: some View {
        TabView {
            Tab("Home", systemImage: "house") {
               HomeView()
            }

            Tab("Messages", systemImage: "message") {
                MessagesView()
            }
                .customizationID("com.swiftlee.tab.messages")
            Tab("Settings", systemImage: "gear") {
                SettingsView()
            }
                .customizationID("com.swiftlee.tab.settings")
        }
        .tabViewStyle(.sidebarAdaptable)
        .tabViewCustomization($customization)
    }
}

There are a few important things to point out:

  • Each tab that you want to allow to be modified needs to have a customizationID modifier set
  • The tab view style needs to be set to sidebarAdaptable
  • A new AppStorage-backed TabViewCustomization property needs to be configured via the tabViewCustomization modifier

Once applied, you can customize the tab items accordingly:

The tab view items become customizable after applying a customization ID.
The tab view items become customizable after applying a customization ID.

As you can see, only the messages and settings tab have become modifiable.

Using tab sections

When you’re using the earlier mentioned sidebarAdaptable tab view style, you can also make use of tab sections inside a SwiftUI TabView.

Imagine having another page that contains archived messages. We could now combine this new page with the existing messages page inside a new tab section:

struct SectionsTabView: View {
    
    var body: some View {
        TabView {
            Tab("Home", systemImage: "house") {
                HomeView()
            }

            TabSection("Messages Related") {
                Tab("Messages", systemImage: "message") {
                    MessagesView()
                }
                Tab("Archive", systemImage: "archivebox") {
                    MessagesArchiveView()
                }
            }

            Tab("Settings", systemImage: "gear") {
                SettingsView()
            }
        }
        .tabViewStyle(.sidebarAdaptable)
    }
}

The Messages Related tab section will show up in the sidebar on iPad, while both tabs will show as normal tabs on an iPhone:

Tab view sections in SwiftUI allow you to order tabs inside sidebars.
Tab view sections in SwiftUI allow you to order tabs inside sidebars.

You can customize sections further by adding section actions or by hiding it for tab bars specifically:

TabSection("Messages Related") {
    Tab("Messages", systemImage: "message") {
        MessagesView()
    }
    Tab("Archive", systemImage: "archivebox") {
        MessagesArchiveView()
    }
}.defaultVisibility(.hidden, for: .tabBar)
    .sectionActions {
        Button("Write new message") {
            // ...
        }
    }

Note that this will only hide the section items inside the tab bar on an iPad while the items remain visible on an iPhone. In this example, the section actions can be a great way to offer quick access to the message composer.

Conclusion

A SwiftUI Tab View is easy to set up for basic use cases and can be modified further when working with sidebars on iPadOS. You can use badges to indicate prominent information directly from within a tab item.

If you’re interested in learning more about other SwiftUI elements, I recommend the following articles:

If you want to improve your SwiftUI knowledge, even more, check out the SwiftUI category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.
Thanks!

 
Antoine van der Lee

Written by

Antoine van der Lee

iOS Developer since 2010, former Staff iOS Engineer at WeTransfer and currently full-time Indie Developer & Founder at SwiftLee. Writing a new blog post every week related to Swift, iOS and Xcode. Regular speaker and workshop host.

Are you ready to

Turn your side projects into independence?

Learn my proven steps to transform your passion into profit.