Scrollable tab bars are elements that you’ll find in many popular apps such as Airbnb and Netflix. In this tutorial, we'll be creating a scrollable tab bar for this imaginary file manager that will allow us to access four different screens without ever having to leave this home screen.
🔻 Download the starter file.
To follow along with this tutorial, download the starter file: Starter srollable tab bar file.judo
Let's dive in!
Starter File Overview
Before we begin, let’s quickly go through the elements on the canvas. The starter file for this tutorial comes with 5 screens. Just like when building an app with a tab bar, the Main screen will act as a container that houses all of the screens accessed through the scrollable tab bar.
Currently, you'll only find an Instance of the Documents screen which, eventually, will only appear when the Documents tab is selected. Later, we’ll be inserting Instances of the Members, Settings, and More Components to the Main screen, so that those screens will appear on the Main screen when tapped on in the scrollable tab.
At the moment, the Main screen only contains a Navigation Stack, which was added so that we could add a Navigation Title, which we applied to the HStack that contains 4 Text layers, one for each clickable tab.
We turned the last tab into a Button that, when tapped on, reveals a Sheet, which you can dismiss by swiping down.
Now that have a good lay of the land, let's build the scrollable tab bar.
1. Create a horizontal scroll container.
Let’s begin by making the HStack, which contains all of our tabs, scrollable. To do so, right-click on the HStack in the Layers panel or on the canvas, and then wrap it in a Scroll View container. Then, from the Inspector panel, change the Axis parameter to Horizontal and hide the scroll bar indicator.
📱 See your design in action.
Send the file over to your mobile device and open it using the Judo mobile app. If you don’t have it yet, you can download it, here.
As you can see, you can now scroll through the layers in the HStack horizontally.
2. Create an ON and OFF state for each tab.
Next we need to create two states for our Documents, Members, and Settings Text layers:
For when the tab is selected
For when the tab is not selected
Documents
Let’s start with the Documents Text layer. Let’s keep this Text layer as is, but update its label in the Layers panel, so that we’ll know that this is the Text layer to use when the tab is not selected. We can keep things simple by tacking on the word "OFF".
Then, right-click to Duplicate the Text layer and change the label to "ON" instead.
You can style the selected state of the tab any way that you want, but I’ll insert the Background modifier from the Inspector panel to add a background to the Text layer. Then, I'll replace the Rounded Rectangle with a Capsule instead. I’ll also change the Color to Blue.
Now let’s go back to the Text layer to add some Padding and adjust the order of the modifiers in the Inspector panel here, so that the Padding gets applied first. Let’s also update the Length to 8 points of Padding instead.
Lastly, let's change the color of the Text layer by applying the Foreground Color modifier. Let’s change it to White.
Members & Settings
Now we need to configure the two states for the Members and Settings Text layers, so let’s start by updating the label for the existing Text layers by adding the word "OFF".
Then, for the ON state, we can right-click on the ON state for the Documents Text layer to Copy it and the Paste in copies of it under the Members and Settings OFF state layers.
★ Pro Tip
Use the keyboard shortcut ⌘C to copy and ⌘V to paste.
For each copy, we’ll need to rename the label in the Layers panel as well as update the Text from the Inspector panel, so that the Members - ON layer displays "Members" and the Settings - ON layer displays "Settings".
⬇ Note
Since a few layers are hiding, you can check to see if you configured each layer properly by adjusting the size of the Main screen from the Inspector panel. When you’re done, you can change the size back to iPhone 14 from the Preview Controls.
3. Create Conditional layers.
To configure which layers should and shouldn’t be visible, we need to turn each Text layer into a Conditional. Conditionals are containers that only display the layers within them when their condition or conditions, if there are several, are met.
For example,
1. | 2. Condition | 3. |
number = 5 | if number equals 5 | 🎉 |
Documents - ON
Let’s begin by creating a Property. To do so, click on the Main screen and then from the Inspector panel, click on the plus (+) icon in the top-right corner of the Properties section. For this example, let’s create a Text Property, though you could configure this with other Property types as well.
Name it tab and give it a default value of documents.
Next, let’s first turn the ON state of the Documents Text layer into a Conditional layer by clicking on the Conditional toolbar button. Since we’re about to have several Conditional layers, be sure to label each one accordingly, so that you don’t get them all mixed up.
To add a Condition, click on the plus (+) icon in the top-right corner of the Condition section in the Inspector panel. Then, when you click on the text box that reads Key path, all of the Component’s Properties will appear. For these Conditional layers, we’ll be working with the tab Text Property that we just created.
Remember how, earlier, we had set the default of the tab property to documents? Let’s change the Condition of this layer to Equals documents, so that the ON state of the Documents Text layer will be visible on the screen.
Documents - OFF
To hide the OFF state of this Documents tab, turn it into a Conditional by right-clicking and selecting Make Conditional. Then, rename it.
When we add a Condition this time, we’re going to set the tab Property to Does Not Equal documents. Since the default value of the tab Property is documents, this layer will not be visible only on the screen, unless its Condition is met.
Members - OFF
Now let’s configure the other two tabs. Since the Documents tab’s visibility is conditional on whether or not the tab Property equals documents, the Members and Settings tab visibility will have to be conditional on another value, such as the word members and settings, respectively.
Let’s start with the Members tab, but this time with the OFF state of the Members tab. After we turn it into a Conditional, let’s add a Condition from the Inspector panel. Unlike the Documents tab, this tab will not be selected at first, so we want this OFF state to be visible instead of the ON state.
Also unlike the Documents tab, this tab will be conditional on whether or not the tab Property equals the word members, so let’s set the tab Property to Does Not Equal the word members. Since the default value of the tab Text Property is documents, the OFF state of the Members tab will still be visible.
Members - ON
Let’s turn the ON state of the Members tab into a Conditional layer, but this time from the Inspector panel, set the Condition to properties.tab Equals members. Since that is not the default value of the tab Text Property, this layer will be hidden until its condition is met.
Settings - OFF
Turn the OFF state of the Settings tab into a Conditional. Then, from the Inspector panel, let’s add a Condition.
Unlike the other two tabs, this tab will be conditional on whether or not the tab Property equals the word settings, so let’s set the tab Property to Does Not Equal the word settings. Since the default value of the tab Text Property is documents, the OFF state of this Settings tab will still visible.
Settings - ON
Turn the ON state of the Settings tab into a Conditional layer, but this time from the Inspector panel, set the Condition to properties.tab Equals settings. Since that is not the default value of the tab Text Property, this layer will be hidden from the screen unless its Condition is met.
More
As you can see here, we have a fourth tab with an icon titled More. We already went ahead and turned this tab into a Button that, when tapped on, reveals a Sheet. If you’re unsure of how we built this modal presentation, we’ve got a great video that you can check out.
📱 Preview your design.
Send the file over to your mobile device and open it using the Judo mobile app to see how the scrollable tab bar works.
As you can see, now only 4 tabs are visible as you scroll through horizontally, but you can’t interact with any of the tabs, except More. That’s because we had turned that tab into a Button.
Let’s go back to the canvas to make the rest of the tabs active.
4. Make each tab tappable
Before we do anything, let’s first figure out which layers we need to turn into a Button.
When we look at our preview, which layer states would we want to click on, ON or OFF? Since a layer in the ON state indicates that that’s the tab that’s been clicked on, we’d want to make the OFF state tabs clickable, so that they could go from being in the OFF state to ON.
So, let’s turn the Text layer inside each OFF Conditional into a Button by selecting it in the Layers panel or on the canvas, and then either clicking on the toolbar button, or right-clicking to select Make Button.
Settings - OFF
Next, let’s configure each Button. When a user will tap on this tab, we’ll want the Text layer to switch to its ON state. So, from the Inspector panel, let’s click on the plus (+) icon in the Actions section, and then click on Dismiss to change the Action to Set the tab Property To settings.
⬇ Note
By changing the default value of the tab Property from documents to settings, the OFF state Conditional layer will hide due to the Condition we had set and instead, the ON state Conditional layer will appear, as the Condition will now match.
As you can see, by default, Buttons are blue, but we can override this by applying the Button Style modifier. There are several styles that you can choose from, but for this example, let’s set it to Plain to change the color back to black.
📱 Preview your design.
Before we move forward with the other tabs, let’s send the file to our mobile device, so that we can see this Button in action.
Since the Settings - OFF state is the only Button that we’ve configured, when we tap on it, the OFF state will hide while the ON state with the blue background will appear.
Members - OFF
Now let's configure the rest of the tabs so that they react the same way. Start by clicking on the Button layer in the Members - OFF state, and from the Inspector panel, let’s add a similar Action, but this time, we’ll Set the tab Property To members.
Then, apply the Button Style modifier to change the Style to Plain.
Documents - OFF
We can do the same thing for the Documents - OFF state, even if we can’t see it on the canvas at this time. Start by selecting the Button layer in the Documents - OFF Conditional, and then from the Inspector panel, add an Action that will Set the tab Property To documents.
Then, apply the Button Style modifier to change the Style to Plain.
📱 Preview your design.
Now that we’ve configured every tab in the horizontal scroll menu, send this file over to your mobile device to see it in action.
As you can see, now every tab is tappable and reacts accordingly.
All that’s missing now is to make the right screen appear upon each tab selection.
5. Make each tab reveal the right screen when tapped.
Documents
Let’s start with the Documents screen Component. As you can see, it’s already been added underneath the scrollable tab bar that we’ve created. To make it only appear whenever the Documents tab is selected, we’ll turn it into—you guessed it—another Conditional layer. Then, rename it to Documents for clarity.
Next, let’s create a Condition from the Inspector panel. Since we only want this screen to appear when the Documents tab is selected, let’s set the Condition to properties.tab Equals documents—just as we had done for the ON state of the Documents tab.
Members
Now let’s do the same thing for the other two tabs. Start by inserting an Instance of the Members screen Component from the Insert (+) menu. If you aren’t familiar with Components and Instances, we’ve got a video that you can check out.
Then, turn it into a Conditional layer once more, rename it, and then add the following Condition: properties.tab Equals members.
⬇ Note
Since the tab Property’s default value is documents, the Members Component Instance is no longer visible and will only appear when the Members tab is set to its ON state.
Settings
Last, but not least, the Settings tab. Insert an Instance of the Settings screen Component by right-clicking to Copy and then using the keyboard shortcut ⌘V to paste it in.
Then, turn it into a Conditional layer, rename it, and then add the following Condition from the Inspector panel: properties.tab Equals settings.
⬇ Note
Since the tab Property’s default value is documents, the Settings Component Instance is no longer visible on the canvas and will only appear when the Settings tab is set to its ON state.
📱 Preview your design.
Now all that’s left is to send the Judo file over to your mobile device to see your scrollable tab bar in action.
Every tab that you click on will not only get a blue capsule background, but it will also show the corresponding screen!
Code Inspector
Before we go, let’s take a quick look at the Code Inspector, to see some of the SwiftUI code that Judo generated for us.
import SwiftUI
struct Main: View {
@State private var isMore: Bool
@State private var tab: String
init(isMore: Bool = false, tab: String = "documents") {
_isMore = State(initialValue: isMore)
_tab = State(initialValue: tab)
}
var body: some View {
NavigationStack {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 19) {
if tab == "documents" {
Text("Documents")
.padding(EdgeInsets(top: 8, leading: 8,
bottom: 8, trailing: 8))
.background(alignment: .center) {
Capsule(
style: .circular
)
.fill(Color(uiColor: .systemBlue))
}
.foregroundColor(Color(uiColor: .white))
}
if tab != "documents" {
Button {
tab = "documents"
} label: {
Text("Documents")
}
.buttonStyle(.plain)
}
// Repeats for each Conditional layer
}
.navigationTitle("Home")
.font(.system(.title3, design: .default))
.padding()
.sheet(isPresented: $isMore) {
More()
.presentationBackground(Color(uiColor:
.systemGroupedBackground))
}
}
if tab == "documents" {
ScrollView(.vertical, showsIndicators: true) {
Documents()
}
}
if tab == "members" {
Members()
}
if tab == "settings" {
Settings()
}
}
}
}
The @State private var tab: String
declares a private state variable called tab
that stores a String
, or a text value. A private state variable in SwiftUI is used to hold and manage data that can change over time, and updates views when that data changes.
Next, init(isMore: Bool = false, tab: String = "documents")
. Just as we had configured on the canvas, the initial value of the tab
property is set to "documents"
.
Lower down, you'll find ScrollView(.horizontal, showsIndicators: false)
, which is the horizontally scrollable container for the tab bar, and it's configured to hide the scroll indicators.
Then, for every conditional layer that we created on the canvas, you’ll find an if
statement that executes a block of code when the specified condition is true. The equality operator (==
) checks to see if the two values are equal while the inequality operator (!=
), checks to see if the two values are not equal.
Take, for example, this if instance: if tab != "documents"
. When the tab
property does not equal the word "documents"
, a Button
gets displayed that, when pressed, will set the tab
to "documents"
.
The Button
label is a Text()
layer that reads "Documents"
and uses the .plain
.buttonStyle
, all of which is what we had configured on the canvas.
If you scroll down lower, you'll also find an if statement that checks if
the tab
property is equal (==
) to "documents"
. If it is, then it will display the Documents()
screen.
Resources
🔻 Download the file.
If you'd like to see the final design, check out this Judo file: Scrollable tab bar.judo