The greatest advantage of SwiftUI is the capability to create interfaces and use specific controls with just a couple of lines; that's the exact opposite of UIKit, where every control needs several lines of code for initialization and configuration.
In previous tutorials I wrote about many SwiftUI controls, (views according to SwiftUI's terminology), and in this post I'm focusing on another one; the Picker view.
Picker is the number one candidate view when users should be provided with a range of options to choose from. In SwiftUI, a picker can get various appearances pretty fast and painlessly; that's really helpful, as different kind of pickers can exist in different places. Configuring a picker does not require much time or effort; and in this post, you're going to read everything you need in order to start using SwiftUI pickers right away.
Displaying a Picker
In the following snippet you can see the simplest way to initialize and present a Picker in a SwiftUI view:
Picker("Color Scheme", selection: $colorScheme) {
Text("Light").tag(0)
Text("Dark").tag(1)
}
The Picker view accepts three arguments:
- A string value as the picker's label.
- A binding value that holds the selection.
- The options that user will choose from, often implemented as a series of Text views.
Focusing on the second point initially, $colorScheme is the binding value of the following @State property:
@State private var colorScheme = 1
The initial value assigned to this property should match to the option that we'd like to show as selected by default. To make that match, we specify tag values to options in the picker's closure. Each option must have a different tag value, which we pass as argument to the tag(_:) modifier right after each view.
Tag values must conform to Hashable protocol, and in this example I chose to use Int values. The initial colorScheme value matches to the tag value of the "Dark" option, and this will be the one automatically selected when the picker will appear.
Picker styles
There are a few different styles that we can apply to a Picker that affect its appearance. If we don't set a specific style, the default one is the menu style. In this case, the default option is displayed as a button. When tapped, a menu with the rest of options is popping up.
For instance, the above few lines of code results to this:

Another style, familiar from the past, is the wheel style. This one presents all available options in a wheel that scrolls towards up or down. To apply that style to the picker, we must explicitly set it as shown right next:
Picker("Color Scheme", selection: $colorScheme) {
...
}
.pickerStyle(WheelPickerStyle())

There is also the segmented style that we may apply to the picker. With it, a segmented control presents the picker's options.
Note
Read about the segmented control in SwiftUI in this tutorial.
Similarly as before, it's necessary to provide the segmented style to the pickerStyle(_:) method in order to use it:
Picker("Color Scheme", selection: $colorScheme) {
...
}
.pickerStyle(SegmentedPickerStyle())

The wheel style is probably the most suitable for displaying a long list of options. With menu and segmented styles, users expect to find a small range of options to choose from.
Picker in Forms
The Picker view is particularly interesting when used in SwiftUI forms. If we don't provide a specific style, then the picker gets a totally different appearance from what we've seen so far. It displays a disclosure indicator next to the selected option, but in addition to that, the label's text is also visible in the leading edge of the row that contains the picker.
On tap, a new view is showing up with all available options in a list, so users can choose from. This new view is created automatically, and it contains all the picker's options in a list. A default navigation is put in motion in order to push and pop that secondary view, and let users go forth and back in order to change their selection.
However, there is a requirement in order for all that to work. We must contain the Form view in a Navigation view, otherwise the picker won't be responding on our tap gestures.
The following code summarizes all that in a few lines. See that the top-level view is a Navigation view, which in turn contains a Form view. The form contains a Section view, and the section finally contains the picker:
NavigationView {
Form {
Section("Color Preferences") {
Picker("Color Scheme", selection: $colorScheme) {
Text("Light").tag(0)
Text("Dark").tag(1)
}
}
}
}
Here's the result of the above few lines:

Listing multiple options
In all previous parts I used the same picker example, which contains two particular options only as hardcoded Text views. In general, doing so for a limited number of options is fine, but what if there's a large number of options to display, or available options change dynamically?
In such cases there is a different path to follow, as we list all available options using a ForEach view.
Let's see a simple example. The following array contains all years as integer numbers from 2000 to 2021. The @State property holds the current value (year) that will be displayed as the default selection when the picker will appear:
var years = Array(2000...2021)
@State private var selectedYear = 2021
The following picker displays all years in a wheel. It makes use of the ForEach view, and the years array is given as argument:
Picker("Pick a year", selection: $selectedYear) {
ForEach(years, id: \.self) {
Text("\($0.formatted(.number.grouping(.never)))")
}
}.pickerStyle(WheelPickerStyle())
The $0.formatted(.number.grouping(.never)) is the new way to work with formatters in iOS 15. Its job in this example is to remove the grouping separator from the displayed numbers, so years won't appear like 2,021 or 2.021 (depending on the locale).

Pickers with custom types
Right above we used ForEach in order to display values of a basic data type (integer) in the picker. However, we can do the same with custom types as well. There is just one requirement; the custom type should conform to the Identifiable protocol.
Suppose that we are developing a game, and we have the following struct that represents a weapon and the damage it can cause. The id property is a requirement coming from the Identifiable protocol, and its purpose is to uniquely identify every Weapon object in a collection:
struct Weapon: Identifiable {
var id = Int.random(in: 0...100)
var name: String
var damage: Int
}
Now, let's create an array with a few weapons, and a @State property with the default weapon of the player:
var weapons = [Weapon(name: "Pistol", damage: 50),
Weapon(name: "Riffle", damage: 70),
Weapon(name: "Shotgun", damage: 72),
Weapon(name: "Machette", damage: 45)]
@State private var currentWeapon = Weapon(name: "Machette", damage: 45)
Giving the option to change weapon using a picker is quite similar to what demonstrated in the previous part. What actually changes is that instead of \.self, we specify the keypath to the property that uniquely identifies each displayed object; the id in this case:
Picker("", selection: $selectedYear) {
ForEach(weapons.reversed(), id: \.id) {
Text("\($0.name) - Damage \($0.damage)")
}
}
Conclusion
Working with the Picker in SwiftUI is easy. We can use it to show both short and long collections of options, while changing its appearance with different styles is straightforward. In addition to that, it's also interesting how picker behaves when used in forms. I hope the few examples presented in the above parts will be useful to you. Thanks for reading, and stay tuned for more content to come.