Beyond buttons: using advanced pickers
In this recipe, we will learn how to implement pickers—namely, Picker, Toggle, Slider, Stepper, and DatePicker. Pickers are typically used to prompt the user to select from a set of mutually exclusive values. Toggle views are used to switch between on/off states. Slider views are used for selecting a value from a bounded linear range of values. As with Slider views, Stepper views also provide a UI for selecting from a range of values. However, steppers use the + and : signs to allow users to increment the desired value by a certain amount. Finally, DatePicker views are used for selecting dates.
Getting ready
Create a new SwiftUI project named UsingPickers.
How to do it…
Let’s create a SwiftUI project that implements various pickers. Each picker will have a @State
variable to hold the current value of the picker. The steps are given here:
- In the
ContentView.swift
file, create@State
variables that will hold the values selected by the pickers and other controls. Place the variables between theContentView
struct and the body:@State private var choice = 0 @State private var showText = false @State private var transitModes = ["Bike", "Car", "Bus"] @State private var sliderVal: Float = 0 @State private var stepVal = 0 @State private var gameTime = Date()
- Replace the body of the
ContentView
struct with aForm
view. Then, add aSection
view and aPicker
view to the form:Form { Section { Picker("Transit Modes", selection: $choice) { ForEach( 0 ..< transitModes.count, id: \.self) { index in Text("\(transitModes[index])") } } .pickerStyle(.segmented) Text("Current choice: \(transitModes[choice])") } }
- Under the existing
Section
view, add anotherSection
view and aToggle
view:Section{ Toggle(isOn: $showText){ Text("Show Text") } if showText { Text("The Text toggle is on") } }
- Add a
Section
view and aSlider
view:Section{ Slider(value: $sliderVal, in: 0...10, step: 0.001) Text("Slider current value \(sliderVal, specifier: "%.1f")") }
- Add a
Section
view and aStepper
view:Section { Stepper("Stepper", value: $stepVal, in: 0...5) Text("Stepper current value \(stepVal)") }
- Add a
Section
view and aDatePicker
view:Section { DatePicker("Please select a date", selection: $gameTime) }
- Add a
Section
view and a slightly modifiedDatePicker
view that only accepts future dates:Section { DatePicker("Please select a date", selection: $gameTime, in: Date()...) }
- The result should be a beautiful form, like what is shown here:
Figure 1.14: SwiftUI Form with Pickers
How it works…
Form
views group controls that are used for data entry, and Section
views create hierarchical view content. Section
views can be embedded inside a Form
view to display information grouped together. The default presentation style for a Form with embedded Section views is to include a gray padding area between each section for visual grouping, as shown in Figure 1.14.
Picker
views are used for selecting from a set of mutually exclusive values. In the following example, a segmented picker is used to select a transit mode from our transitModes
state variable:
Picker("Transit Modes", selection: $choice) {
ForEach( 0 ..< transitModes.count, id:\.self) { index in
Text("\(transitModes[index])")
}
}
.pickerStyle(.segmented)
As shown in the preceding example, a Picker
view takes two parameters, a string describing its function, and a state variable that holds the value selected. The state variable should be of the same type as the range of values to select from. In this case, the ForEach
loop iterates through the transitModes
array indices. The value selected would be an Int
within the range of transitModes
indices. The transit mode located in the selected index can then be displayed using Text("\(transitModes[index])")
. It is also worth noting that we need to apply a .segmented
style to the picker using the .pickerStyle()
modifier, to use the visual segmented style everyone is used to in iOS.
Toggle
views are controls that switch between “on” and “off” states. The state variable for holding the toggle selection should be of the Bool
type. The section with the Toggle
view also contains some text. The @State
property of the Toggle
reflects the current state of the toggle.
Creating a slider requires three arguments:
value
: The@State
variable to bind the user input toin
: The range of the sliderstep
: By how much the slider should change when the user moves it
In the sample code, our slider moves can hold values between 0 and 10, with a step of 0.001.
Steppers take three arguments too—a string for the label, value
, and in
. The value
argument holds the @State
variable that binds the user input, and the in
argument holds the range of values for the stepper.
In this recipe, we also demonstrate two applications of a date picker. The first from the top shows a date picker whose first argument is the label of DatePicker
, and the second argument holds the state variable that binds the user input. Use it in situations where the user is allowed to pick any date without restriction. The other date picker contains a third parameter, in
. This parameter represents the date range the user can select.
Important Note
The @State
variables need to be of the same type as the data to be stored. For example, the gameTime
state variable is of the Date
type.
Picker styles change based on its ancestor. The default appearance of a picker may be different when placed within a form or list instead of a VStack
or some other container view. Styles can be overridden using the .pickerStyle()
modifier.