Implementing tab bar in a SwiftUI app
Learn how to implement tab bar navigation with SwiftUI on iOS, iPadOS, macOS and visionOS.
When designing our app, we must carefully choose the components to implement, understand their purpose, and determine when and how they should be used. One of the most commonly used components for app navigation is the tab bar. This enables what is often called flat navigation, allowing users to access different sections of the app by simply tapping on a specific tab.
With the SwiftUI framework, implementing this type of component is as straightforward as it gets because developers can use the built-in tab bar with just a few lines of code. This not only simplifies our work but also ensures users enjoy a consistent experience across all apps installed on their devices.
Of course, different devices require different kinds of experiences. We will have an overview of how tabs behave in the different operating systems and how to implement the tab bar within an app.
Tabs on iOS
On iOS, the tab bar is located at the bottom of the screen, providing users with easy access to different tabs at their fingertips.
The selected tab is highlighted with the accent color specified in the Asset folder of your Xcode project, while unselected tabs remain in their default appearance.
Tabs on iPadOS
On iPadOS, the tab bar has received a significant update, completely transforming its appearance within apps. It is now positioned at the top of the screen with a capsule layout, offering an experience similar to what is seen on tvOS. It includes a button that converts the tabs into a sidebar.
Tabs on macOS
Since macOS is a desktop-class operating system, tab bars are less common on this platform where in most cases, the sidebar is preferred.
However, some apps, like the Clock app on macOS, use tab bars with a segmented controller appearance, with the different sections located at the top of the window.
Tabs on visionOS
On visionOS, a tab bar is always vertical, floating in a position that’s fixed relative to the window’s leading side. When people look at a tab bar, it automatically expands. To access a specific tab, people gaze at the tab and tap. While a tab bar is expanded, it can temporarily obscure the content behind it.
Implementation
When we want to implement this kind of navigation within our app using SwiftUI the only thing that developers have to do is use the TabView
container.
TabView {
...
}
Within the TabView
container we can specify each tab just by using the Tab
object. In the Tab
initializer, you can pass a label and also an SF Symbol that will identify each tab in the tab bar.
TabView {
Tab("Flights", systemImage: "airplane.departure") {
FlightsView()
}
Tab("Luggage", systemImage: "suitcase") {
LuggageView()
}
}
One thing that you need to consider while designing your app is also the number of tabs within the tab bar. On some platforms, like iOS, there is a limit of 5 elements. When going over 5 elements the other tabs will be presented in a new tab called More, which will present an additional tab in a separate screen.
In addition to the standard initializer, we can pass an TabRole
value defining the purpose of the tab. At the moment just the search role is available. Every tab with the search role have the magnifying glass SF Symbol and is placed as a last element on the right of the tab bar, keeping consistency among the different apps in the operating system.
Tab(value: .search, role: .search) {
// ...
}
Tabs are also a meaningful way to tell the user the presence of a notification or something to do in a specific tab. To do that there is a simple modifier named badge
that will applied directly on the tab.
TabView {
Tab("Flights", systemImage: "airplane.departure") {
FlightsView()
}
.badge(1)
Tab("Luggage", systemImage: "suitcase") {
LuggageView()
}
}
Starting from iOS 18 we can also set a custom image for each tab, allowing developers to deeply customize this component providing an alternative experience for the user. Here’s an example:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
Tab("Item one", image: "make-it-accessible") {
Text("Item one")
}
Tab("Item one", image: "make-it-spatial") {
Text("Item two")
}
Tab("Item one", image: "make-it-intelligent") {
Text("Item three")
}
}
}
}
Additionally to automatically enable the accent color for your custom symbol set the "Render As" option as "Template Image", you can find this option in the Attribute Inspector section.
The advantage of implementing tab bars using the native components offered by SwiftUI goes beyond the fact that you just need a few lines of code to get it working in multiple operating systems. Native components offer full support for the accessibility features of the system. Always keep that in mind when evaluating the need for creating custom components for your apps.