A slider consists of a handle that the user moves between two extremes of a linear “track”. The ends of the track represent the minimum and maximum possible values. As the user moves the handle, the slider updates its bound value.
- Single Value
- Range Values
- Styling
- Advanced Layout and
CompactSliderState
The following example shows a slider bound to the speed value in increments of 5. As the slider updates this value, a bound Text view shows the value updating.
@State private var speed = 50.0
var body: some View {
CompactSlider(value: $speed, in: 0...100, step: 5) {
Text("Speed")
Spacer()
Text("\(Int(speed))")
}
}
When used by default, the range of possible values is 0.0...1.0:
@State private var value = 0.5
var body: some View {
CompactSlider(value: $value) {
Text("Value")
Spacer()
String(format: "%.2f", value)
}
}
Using the direction:
parameter you can set the direction in which the slider will indicate the selected value:
@State private var value = 0.5
var body: some View {
CompactSlider(value: $value, direction: .center) {
Text("Center")
Spacer()
String(format: "%.2f", value)
}
}
The slider allows you to retrieve a range of values. This is possible by initialising the slider with the parameters from:
and to:
.
The following example asks for a range of working hours:
@State private var lowerValue: Double = 8
@State private var upperValue: Double = 17
var body: some View {
HStack {
Text("Working hours:")
CompactSlider(from: $lowerValue, to: $upperValue, in: 6...20, step: 1) {
Text("\(zeroLeadingHours(lowerValue)) — \(zeroLeadingHours(upperValue))")
Spacer()
}
}
}
private func zeroLeadingHours(_ value: Double) -> String {
let hours = Int(value) % 24
return "\(hours < 10 ? "0" : "")\(hours):00"
}
The slider supports changing appearance and behaviour. In addition to the standard style, the Prominent style is also available.
To implement your own style, you need to implement the CompactSliderStyle
protocol, which contains many parameters that allow you to define the view according to user events. The styles are implemented in the same pattern as ButtonStyle.
CompactSliderStyleConfiguration
properties:
The following example shows how to create your own style and use the configuration:
public struct CustomCompactSliderStyle: CompactSliderStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(
configuration.isHovering || configuration.isDragging ? .orange : .black
)
.background(
Color.orange.opacity(0.1)
)
.accentColor(.orange)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
public extension CompactSliderStyle where Self == CustomCompactSliderStyle {
static var `custom`: CustomCompactSliderStyle { CustomCompactSliderStyle() }
}
And now we can apply it:
@State private var value: Double = 0.5
var body: some View {
CompactSlider(value: $value) {
Text("Custom Style")
Spacer()
Text(String(format: "%.2f", value))
}
.compactSliderStyle(.custom)
}
The slider consists of several secondary elements, which can also be defined within their own style or directly for the slider.
-
The
.compactSliderSecondaryColor
modifier allows you to set the color and opacity for the secondary slider elements. You can simply change the base color for secondary elements:.compactSliderSecondaryColor(.orange)
. -
Using the other signature of the modifier: `.compactSliderSecondaryColor', the color can be set individually for each secondary element.
-
Another modifier
.compactSliderSecondaryAppearance
gives you the ability to change theShapeStyle
for the progress view.
Let's take the previous example and change the secondary elements:
public struct CustomCompactSliderStyle: CompactSliderStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(
configuration.isHovering || configuration.isDragging ? .orange : .black
)
.background(
Color.orange.opacity(0.1)
)
.accentColor(.orange)
.compactSliderSecondaryColor(
.orange,
progressOpacity: 0.2,
handleOpacity: 0.5,
scaleOpacity: 1,
secondaryScaleOpacity: 1
)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
Now change the solid color of the progress view to a gradient using the `compactSliderSecondaryAppearance' modifier:
public struct CustomCompactSliderStyle: CompactSliderStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(
configuration.isHovering || configuration.isDragging ? .orange : .black
)
.background(
Color.orange.opacity(0.1)
)
.accentColor(.orange)
.compactSliderSecondaryAppearance(
progressShapeStyle: LinearGradient(
colors: [.orange.opacity(0), .orange.opacity(0.5)],
startPoint: .leading,
endPoint: .trailing
),
focusedProgressShapeStyle: LinearGradient(
colors: [.yellow.opacity(0.2), .orange.opacity(0.7)],
startPoint: .leading,
endPoint: .trailing
),
handleColor: .orange,
scaleColor: .orange,
secondaryScaleColor: .orange
)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
One of the slider parameters allows you to control the visibility of the handle. For this gradient example, it can be disabled:
CompactSlider(value: $value, handleVisibility: .hidden) {
Text("Custom Style")
Spacer()
Text(String(format: "%.2f", value))
}
.compactSliderStyle(.custom)
Prominent style allows for a more dramatic response for the selected value. It requires two colors, which determine the accent color depending on the progress of the selected value. You can also optionally apply a background gradient based on these colors.
@State private var chooseSide: Double = 0.5
@State private var temperature: Double = 20
var body: some View {
VStack(alignment: .leading, spacing: 24) {
// 1.
CompactSlider(value: $chooseSide, direction: .center) {
Text("Red")
Spacer()
Text("Blue")
}
.compactSliderStyle(
.prominent(
lowerColor: .red,
upperColor: .blue
)
)
// 2.
HStack {
Text("Temperature:")
CompactSlider(value: $temperature, in: -10...30, step: 2) {}
.compactSliderStyle(
.prominent(
lowerColor: .blue,
upperColor: .orange,
useGradientBackground: true
)
)
Text("\(Int(temperature))℃")
.frame(width: 50, alignment: .trailing)
}
}
}
The slider provides a state that can be used for more advanced layouts. To do this, you must subscribe for state changes via bindings.
In the following example, we will show the value in the progress position:
@State private var value: Double = 0.5
@State private var sliderState: CompactSliderState = .zero
var body: some View {
ZStack {
CompactSlider(value: $value, state: $sliderState) {}
Text("\(Int(100 * value))%")
.foregroundColor(.white)
.padding(6)
.background(
Capsule().fill(Color.blue)
)
.offset(x: sliderState.dragLocationX.lower)
.allowsHitTesting(false)
}
}