How to create a custom Segmented Control with SwiftUI

Andras Pal
3 min readJan 13, 2021

--

Creating a simple segmented control is fairly easy with SwiftUI, you just need a picker that has a binding to a property that determines the currently-selected option and has a/some views for the labels as selectable options. Once you have this, you just need to add a pickerStyle and set it to SegmentedPickerStyle.

We can use texts and images but our options are really limited and even though most of the other styles like buttonStyles or toggleStyles are customizable and we can create our own versions, it’s not possible to do the same with the pickerStyle.

In this article we will explore how we can build our own SegmentedControl to overcome these problems.

First steps

Create a new SwiftUI project and make a new file called CustomSegmentedControl.swift for the new segmented control. We will design the control in this file, set up the initializer and use the control in the very same way as we use the Picker.

Right now we have a simple grey RoundedRectangle() - this will be the background of the whole component. Try not to use paddings around your main stack when you make components like this. The view should fill the space, otherwise you’ll have difficulties later when you need to calculate the size of different parts. You can always add paddings or set up the size of your component when you are actually using it, as you can see it in the Preview.

Making the labels

We need a subview for the labels, so we can easily create them later in the body of the segmented control with a ForEach. Each label has a title, a width and a textColour for simplicity, but feel free to come up with beautiful or even crazy designs later if you want. You can add this struct to the file you created for the segmented control, just keep it outside of the CustomSegmentedControlView.

We want to have a nice big tappable area, that’s where the .contentShape(Rectangle()) comes in. Without it, only the text itself is tappable and that can make it really hard to use the control.

We also need to calculate the width of the segments. For that, we need to divide the width of the control with the number of the labels. After this, we can create our segments with a ForEach. Our view should look like this now:

Adding the selection indicator

We need to drop a RoundedRectangle() between the background shape and the labels. It can use the segmentWidth function to find out the width and we can use size.height for the height. Just to keep it simple, make it .black. To make it move, we will need to change its offset, but first, we need a binding to determine the currently-selected option (like we do it with the Picker).

Add @Binding public var selection: Int to your properties, and add an .onTapGesture to the SegmentLabels in the ForEach and update the selection to match the id of the segment. For the offset we need a new function that multiplies the width value with the selection. You can even add an animation to the selection indicator.

This is our view now:

Adding the dividers

For the dividers we can make a new function and use that in a ForEach between the background shape and the selection indicator. Don’t forget that the Divider has a 1px width and we don’t need it everywhere. Its visibility depends on the position of the selection indicator.

Last steps

We are almost finished now, just need to switch back to the ContentView, add a state variable to control the selection and you can drop the segmented control in the body:

You can find the whole project here with more comments, documentation and a working prototype with switching between different views: https://github.com/zulzu/CustomSwiftUISegmentedControl

--

--