Using Segmented Control in SwiftUI

Posted in SwiftUI

Updated on March 4th, 2021

⏱ Reading Time: 4 mins

A quite often used control in iOS apps is the segmented control, and implementing one in SwiftUI is a breeze. What makes it interesting is the fact that a segmented control is actually a picker! So, if you know how to create a picker, then you already know how to create a segmented control.

To showcase how to implement a segmented control, consider the following piece of code. It adds an Image to a SwiftUI view:

Original Image

In the last line of the above code there is a call to the clipShape modifier in order to apply a custom shape mask to the image.

Note: You can find more about the clipShape modifier in this post.

Suppose that we want to make that mask dynamic, and instead of the Rectangle shape to use the following custom one:

The ShapeOnDemand type conforms to the Shape protocol, which requires to implement the path(in:) method. In it, the path of a different shape is being returned depending on the value of the shapeIndex property.

Let’s make it possible to change shape on the fly now using a segmented control. Back in the view’s body we need to initialize a Picker, but before doing so it’s necessary to declare a property marked with the @State property wrapper that will be indicating the preferred shape in the view:

We can now create a Picker instance which will become the segmented control:

The above is not enough though; what will actually change it into a segmented control is to apply the pickerStyle view modifier, specifying the SegmentedPickerStyle as the desired one:

We can now provide the values of the control’s segments:

Even though the above will show perfectly the control’s segments, it will have no effect when switching from one to another. There must be done some sort of a matching between the shape described by each text, and the value that the selectedShape property should get.

Thankfully, that’s really easy to do using the .tag modifier:

Appending the tag on each Text above will result in setting the value 0 on selectedShape when the first segment is selected, value 1 when the Rounded segment is selected, and so on.

In order for all that to work, there is one last change necessary to be done in the original demo code that sets the shape mask. We have to replace this:

with this:

See that the binding value of the selectedShape property is provided to the ShapeOnDemand initializer. That means that every time its value gets changed, the proper shape will be returned from the ShapeOnDemand instance.

After applying some padding, here is the segmented control working:

Using an array of values

In the above example we specified four different Text views that match to each segment of the segmented control. However, by containing all displayable values into an array we can avoid doing that and manage to have just one Text.

To see that in action, let’s declare the following collection first with all the desired shapes in the necessary order:

In the Picker’s content now, we will use a ForEach in order to iterate through all values of the shapes collection and create a Text for each one:

The highlighted lines in the above snippet is the replacement for the four Texts we had used earlier.

But wait a second; what about the matching between the segments and the selectedShape value?

That’s a missing piece, and once again we will use the tag modifier as above. This time, however, we won’t hardcode any specific value. Instead, we will get the index of each text in the shapes array on the fly. See the highlighted line right next:

The segmented control will work as expected once again after doing that last but crucial addition.

Overriding default colors

Unfortunately, there is no SwiftUI-native way to change the default colors of the segmented control. The solution that will let us override the default colors is to change the appearance of the UISegmentedControl, like we would have done in a UIKit based application.

In order to achieve that, it’s necessary to explicitly define the init() method of the view we are working on:

Inside the init() body, we will specify the tint color of the selected segment, the text color in normal state, and the text color in selected state. Here is how all that is done:

The results of the above code are shown right below:

Summary

Implementing a segmented control and changing the view contents depending on the selection is an easy task. I showed you two different ways to populate data in segments; and along with that how to match the selected segment with a @State property. And as a final touch, how to apply custom colors and change the default appearance.

Thank you for taking the time to read this post! See you soon.

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.