Segmented Control in SwiftUI
Segmented control from UIKit is rolled into Picker
view in SwiftUI
. With the help of pickerStyle
modifier, we can get our good old segmented control back.
Picker is created by providing a selection binding, label, and content for the picker to display and pickerStyle
of type .segmented
turns default picker into a segmented control.
Let’s create an example to understand this a little better.
We will start with a State variable which will hold the current selection for the picker. We will use tag function to identify different values of picker view’s items. This tag value can be any hashable
type and upon selection will modify our State variable which eventually will trigger UI update for the view. Next we will use pickerStyle
to change our picker into a segmented control.
struct PickerExample: View {
@State private var selected = 0
var body: some View {
Picker("Choose course", selection: $selected) {
Text("SwiftUI")
.tag(0)
Text("UIKit")
.tag(1)
}
.pickerStyle(.segmented)
}
}

Using Strings for Selection
In previous example, we saw use of selection State variable to track currently selected value in segmented control. In that case selection variable stored integer type value but we can change that to use string type as well.
We will update our previous example as shown below:
struct PickerExample: View {
@State private var selected = "SwiftUI"
var body: some View {
Picker("Choose course", selection: $selected) {
Text("SwiftUI")
.tag("SwiftUI")
Text("UIKit")
.tag("UIKit")
}
.pickerStyle(.segmented)
}
}

Using enum for Selection
We can use enum
, bind that to selection and use it with picker segment view as shown below:
enum Category: String {
case swiftUI = "SwiftUI"
case uIKit = "UIKit"
}
struct PickerExample: View {
@State private var selected = Category.swiftUI
var body: some View {
Picker("Choose course", selection: $selected) {
Text(Category.swiftUI.rawValue)
.tag(Category.swiftUI)
Text(Category.uIKit.rawValue)
.tag(Category.uIKit)
}
.pickerStyle(.segmented)
}
}

ForEach and Segment Control
So far we have been using individual text views to display as segment for our view but if we have collection to iterate over then we can do that too, with the help of ForEach
.
struct PickerExample: View {
@State private var selected = "SwiftUI"
let segments = ["SwiftUI", "UIKit", "ML", "CV"]
var body: some View {
Picker("Choose course", selection: $selected) {
ForEach(segments, id:\.self) { segment in
Text(segment)
.tag(segment)
}
}
.pickerStyle(.segmented)
}
}

Customization to SegmentControl
There are only few customization options available for the segment control that comes out of box. We can set backgroundColor
, cornerRadius
, padding
etc for the view but not the foregroundColor
or foregroundStyle
doesn’t work yet.
struct PickerExample: View {
@State private var selected = "SwiftUI"
let segments = ["SwiftUI", "UIKit", "ML", "CV"]
var body: some View {
Picker("Choose course", selection: $selected) {
ForEach(segments, id:\.self) { segment in
Text(segment)
.tag(segment)
}
}
.pickerStyle(.segmented)
.background(Color.orange)
.cornerRadius(50)
.padding()
} }

Custom Segmented Control
Even though we don’t have much customization available out of the box but we can easily build a custom segmented control by combining some of the existing SwiftUI views as shown in the code below:
struct CustomSegmentedPickerView: View {
@State private var selection = 0
private var titles = ["SwiftUI", "UIKit", "ML", "CV"]
private var colors = [Color.purple, Color.blue, Color.pink, Color.green]
@State private var frames = Array<CGRect>(repeating: .zero, count: 4)
var body: some View {
VStack {
ZStack {
HStack(spacing: 10) {
ForEach(self.titles.indices, id: \.self) { index in
Button(action: { self.selection = index }) {
Text(self.titles[index])
.foregroundColor(.white)
}.padding(EdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20)).background(
GeometryReader { geo in
Color.clear.onAppear { self.setFrame(index: index, frame: geo.frame(in: .global)) }
}
)
}
}
.background(
Capsule().fill(
self.colors[self.selection].opacity(0.4))
.frame(width: self.frames[self.selection].width,
height: self.frames[self.selection].height, alignment: .topLeading)
.offset(x: self.frames[self.selection].minX - self.frames[0].minX)
, alignment: .leading
)
}
.animation(.default, value: selection)
.background(Capsule().stroke(Color.purple, lineWidth: 3))
Text("Value: \(self.titles[self.selection])")
.font(.largeTitle)
Picker(selection: self.$selection, label: Text("What is your favorite color?")) {
ForEach(0..<self.titles.count) { index in
Text(self.titles[index]).tag(index)
}
}.pickerStyle(SegmentedPickerStyle())
}
}
func setFrame(index: Int, frame: CGRect) {
self.frames[index] = frame
}
}

With that we have reached the end of this article. Thank you once again for reading. If you liked this, don’t forget to 👏 and follow 😍. Also subscribe to our weekly newsletter at https://www.devtechie.com