CompactSlider
is a control for selecting a value from a bounded linear range of values.
The slider is a replacement for the build-in slider and is designed specifically for SwiftUI. For me, the main motivation for writing a component that already exists is the very slow performance under macOS (e.g. when you need to resize the screen with multiple sliders or when animating) and the severely outdated design. At the same time, I was inspired by the slider design that Apple's Photos app developed, which makes heavy use of sliders.
- Requirements
- Installation
- Preview
- Documentation
- Usage
- Styling
- Advanced Layout and
CompactSliderState
- License
Requirements
- Swift 5.6
- Xcode 13
- SwiftUI 2
- macOS 11
- iOS 14
- watchOS 7
Some of the requirements could be reduced if there is a demand for them.
Installation
- In Xcode go to
File
⟩Add Packages...
- Search for the link below and click
Add Package
https://github.com/buh/CompactSlider.git
- Select to which target you want to add it and select
Add Package
Preview
macOS
macOS.mov
iPadOS
iPad.mov
iOS
iOS.mov
watchOS
watchOS.mov
Usage
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
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)
}
}
Range values
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"
}
Styling
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.
Configuration
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 : .gray
)
.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)
}
Secondary Color
The .compactSliderSecondaryColor
modifier allows you to set the color and transparency for the additional slider elements, such as the progress color of the selected value, when there is no hovering or dragging of the slider.
By default, you can simply change the base colour for secondary elements: .compactSliderSecondaryColor(.orange)
But if this is still not enough, you can change the transparency of the secondary elements. Take the previous example and complement the secondary elements with orange as well:
public struct CustomCompactSliderStyle: CompactSliderStyle {
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(
configuration.isHovering || configuration.isDragging ? .orange : .gray
)
.background(
Color.orange.opacity(0.1)
)
.accentColor(.orange)
.compactSliderSecondaryColor(
.orange,
progressOpacity: 0.2,
handleOpacity: 0.5,
scaleOpacity: 1,
secondaryScaleOpacity: 1
)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
Prominent Style
Prominent style allows for a more dramatic response for the selected value.
@State private var speed: Double = 0
var body: some View {
HStack {
Text("Speed:")
CompactSlider(value: $speed, in: 0...180, step: 5) {}
.compactSliderStyle(
.prominent(
lowerColor: .green,
upperColor: .red,
useGradientBackground: true
)
)
Text("\(Int(speed))")
}
}
Advanced Layout
License
CompactSlider
is available under the MIT license