CompactSlider is a SwiftUI control for macOS, iOS and watchOS.

Overview

cover3

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

  • 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

  1. In Xcode go to FileAdd Packages...
  2. Search for the link below and click Add Package
https://github.com/buh/CompactSlider.git
  1. 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.

Speed

@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:

Direction

@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:

Range Values

@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:

Configuration

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)
}

custom_style@2x

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))
    }
}

custom_style2@2x

Prominent Style

Prominent style allows for a more dramatic response for the selected value.

Prominent Style

@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

Comments
  • CompactSlider's action not working inside a Form

    CompactSlider's action not working inside a Form

    Describe the bug

    Both Tap&Drag is not working when CompactSlider is inside in a Form.

    Normal Slider works properly.

    To Reproduce Steps to reproduce the behavior:

    struct DemoView: View {
        @State private var progress = 0.0
        var body: some View {
    		Form {
    		    Section {
    		        Slider(value: $progress, in: 0.0 ... 1.0, step: 0.001) {
    		            Text("Progress \(progress, format: .percent)")
    		        }
    		        CompactSlider(value: $progress, in: 0.0 ... 1.0, step: 0.01) {
    		            Text("Progress")
    		            Spacer()
    		            Text("\(progress, format: .percent)")
    		        }
    		    }
    		}
        }
    }
    

    Expected behavior

    Both Slider and CompactSlider work properly.

    Screenshots

    IMG_0407

    Platform and version:

    • OS: iOS 16.1 beta 3
    • Device: iPhone 14 Pro Max

    Additional context

    It's working on Simulator but not on real device.

    If CompactSlider is in a normal VStack, it will also work properly.

    opened by Kyle-Ye 10
  • Turn off tap to position on slider

    Turn off tap to position on slider

    Describe the bug If you are using this slider control and you tap anywhere on the slider the control will go to that position and respond with the new value. However, if you have this control in a window with other sliders in Grid or List view and are scrolling then this is not the behavior you need.
    To Reproduce Steps to reproduce the behavior:

    Expected behavior this is a nice feature.. sometimes... we need more control and have the ability to turn that off.

    Also, it would be nice if there was a way to always show indicator marks.

    Platform and version:

    • OS: iOS 15, iPadOS 15
    • Device: iPhone XS, iPad Pro
    opened by doodahdayz 7
  • How to detect when dragging or editing has stopped

    How to detect when dragging or editing has stopped

    This slider looks great and I love the ability to configure it... but I cannot find a way to only execute a function / closure when the user has stopped dragging... if you want to use the value of the slider to make a network call you surely don't want to be firing off requests as the user is dragging the slider around.. Is there anyway to know when "editing" has stopped with this slider?

    opened by doodahdayz 7
  • The Slider Handle does not appear in the right place.

    The Slider Handle does not appear in the right place.

    Describe the bug The Slider Handle does not appear in the right place when the value is changed with onAppear() after the view is created.

    To Reproduce

    1. Create the ViewModel:
    @MainActor final class TestViewModel: ObservableObject {
        let serviceContainer: ServiceContainerProtocol
    
        ...
    
        @Published var testValue: Double = 0.0
        var maxValue: Double = 0.0
    
        ...
    
        init(serviceContainer: ServiceContainerProtocol) {
            self.serviceContainer = serviceContainer
    
           ...
    
        }
    }
    
    1. Create the View:
    struct TestView: View {
        @StateObject private var viewModel: TestViewModel
    
        ...
    
        init(serviceContainer: ServiceContainerProtocol) {
            self._viewModel = StateObject(wrappedValue: TestViewModel(serviceContainer: serviceContainer))
    
            ...
    
        }
    
        var body: some View {
            ...
    
            VStack(spacing: 0) {
                CompactSlider(value: $viewModel.testValue, in: 0 ... viewModel.maxValue, step: 1) {
                    Text("Lehtar Payı")
                    Spacer()
                    Text("%\(String(format: "%.0f", viewModel.testValue))")
                }
                .compactSliderStyle(.custom)
            }
            .onAppear {
                withAnimation {
                    viewModel.testValue = 50 // Value will come somewhere else. Its dynamic.
                    viewModel.maxValue = 100 // Value will come somewhere else. Its dynamic.
                }
            }
    
            ...
    
        }
    }
    

    Expected behavior The Slider Handle should appear right in the middle.

    Screenshots Current Behavior: Current Behavior

    Expected Behavior: Expected Behavior

    Platform and version:

    • OS: iPadOS 15.5
    • Device: iPad Pro (9.7-inch)

    Workaround The Slider Handle appears in the right place when using the asyncAfter() function.

    .onAppear {
        withAnimation {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                viewModel.testValue = 50 // Value will come somewhere else. Its dynamic.
            }
    
            viewModel.maxValue = 100 // Value will come somewhere else. Its dynamic.
        }
    }
    
    opened by wynioux 2
  • How to get slide value when hover slide?

    How to get slide value when hover slide?

    Hi, I want to add preview video just like Youtube. As you can see if you hover you mouse over the slider, Youtube show an thumbnails and current time. to support this feature, I think slider should get slide current value when hover.
    How can CompactSlider achieve this ?

    image
    opened by yujinqiu 1
  • CompactSliderState not working on iPhone

    CompactSliderState not working on iPhone

    I understand that CompactSliderState is working on Mac and iPad, but we need a way to know when user done, like onEditingChanged on the SwiftUi Slider, or when onEnding when drag...

    Thank you

    opened by iTarek 1
Releases(1.1.2)
  • 1.1.2(Oct 3, 2022)

    • Fixed slider not behaving correctly when it is placed inside scroll view for iOS/iPadOS/watchOS.
    • Fixed accidental tap on slider which changes its value immediately.
    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(May 18, 2022)

  • 1.1.0(May 11, 2022)

    Haptic feedback

    Haptic feedback will now be enabled by default for the snapped slider or for edge values. It can be disabled with the following modifier:

    @State private var value = 0.5
    
    var body: some View {
        CompactSlider(value: $value, step: 0.1) {
            Text("Int(value * 100)%") 
        }
        .compactSliderDisabledHapticFeedback(true)
    }
    

    Demo app

    Another custom style has been added to the demo app.

    example

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(May 8, 2022)

    First release! 🚀 All in all, it turned out to be quite comprehensive, which I hadn't quite planned for. But in the process of writing documentation and various user requests I found some interesting improvements, which I decided to add to the first release as well. I hope the slider will be useful to you!

    Source code(tar.gz)
    Source code(zip)
Owner
Alexey Bukhtin
iOS developer
Alexey Bukhtin
A beautiful slider control for iOS built purely upon Swift

SnappingSlider A beautiful slider control for iOS. Installation There are two ways to add the control to your project; you can add it as a submodule i

Rehat Kathuria 577 Dec 15, 2022
VerticalSlider is a vertical slider control for iOS in Swift.

?? VerticalSlider If you like VerticalSlider, give it a ★ at the top right of this page. Overview VerticalSlider is a vertical implementation of the U

Jon Kent 78 Sep 15, 2022
A custom reusable circular / progress slider control for iOS application.

HGCircularSlider Example To run the example project, clone the repo, and run pod install from the Example directory first. You also may like HGPlaceho

Hamza Ghazouani 2.4k Jan 4, 2023
iOS 11 Control Center Slider

SectionedSlider Control Center Slider Requirements Installation Usage License Requirements iOS 8.0+ Swift 3.0+ Xcode 8.0+ Installation CocoaPods Cocoa

Leonardo Cardoso 361 Dec 3, 2022
VolumeControl is a custom volume control for iPhone featuring a well-designed round slider.

#VolumeControl VolumeControl is a custom volume control for iPhone featuring a well-designed round slider. Preview Usage // Include VolumeControl.h in

12Rockets 81 Oct 11, 2022
A custom reusable slider control with 2 thumbs (range slider).

MARKRangeSlider A custom reusable slider control with 2 thumbs (range slider). Values range is between minimumValue and maximumValue (from 0 to 1 by d

Vadym Markov 181 Nov 21, 2022
A feature-rich circular slider control written in Swift.

MTCircularSlider Screenshot Usage To run the example project, clone the repo, and run pod install from the Example directory first. Requirements iOS 1

Eran Boudjnah 132 Jan 1, 2023
A reusable Slider View made with SwiftUI

ZSlider A reusable Slider View made with SwiftUI. Installation: Minimum version iOS 13 In Xcode go to File -> Swift Packages -> Add Package Dependency

null 7 Dec 15, 2022
SwiftUI Sliders with custom styles

Custom SwiftUI sliders and tracks. This package allows you to build highly customizable sliders and tracks for iOS, macOS and Mac Catalyst. Features B

SpaceNation 532 Jan 8, 2023
TriggerSlider is a simple SwiftUI trigger slider

TriggerSlider is a simple SwiftUI trigger slider which can be used instead of a button, e.g. in a situation where the

Dominik Butz 4 Dec 16, 2022
SwiftUI Clock Time Picker

SwiftUI Clock Time Picker example An exampe of using ClockTimePicker library. Clock with hands ClockTimePicker is a SwiftUI view that displays a clock

workingDog 7 Dec 26, 2022
Dragger - A Customizable Drag Slider swiftUI view

Dragger A customizable SwiftUI Dragger view. Installation In Xcode add https://g

Kiefer Wiessler 4 Dec 19, 2022
PhotoSlider is a simple photo slider and can delete slider with swiping.

PhotoSlider is a simple photo slider and can delete slider with swiping.

Daichi Nakajima 247 Nov 30, 2022
Custom & highly configurable seek slider with sliding intervals, disabled state and every possible setting to tackle!

iLabeledSeekSlider Custom & highly configurable seek slider with sliding intervals, disabled state and every possible setting to tackle! Minimum iOS v

Edgar Žigis 9 Aug 16, 2022
Simple and light weight slider with chapter management

Simple and light weight slider with chapter management Demo Installation CocoaPods WESlider is available through CocoaPods. To install it, simply add

Lucas Ortis 89 Nov 29, 2022
A slider, similar in style to UISlider, but which allows you to pick a minimum and maximum range.

TTRangeSlider A slider, similar in style to UISlider, but which allows you to pick a minimum and maximum range. Installation TTRangeSlider is availabl

Tom Thorpe 952 Dec 2, 2022
A powerful Circular Slider. It's written in Swift, it's 100% IBDesignable and all parameters are IBInspectable.

CircularSlider A powerful Circular Slider. It's written in Swift, it's 100% IBDesignable and all parameters are IBInspectable. Demo Installation Circu

Matteo Tagliafico 253 Sep 19, 2022
UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizontal.

MultiSlider UISlider clone with multiple thumbs and values, range highlight, optional snap intervals, optional value labels, either vertical or horizo

Yonat Sharon 326 Dec 29, 2022
VerticalSlider - An animatable and customizable vertical slider written in Swift 4

VerticalSlider An animatable and customizable vertical slider written in Swift 4. Quick Start VerticalSliderPlayground Clone Repo Open VSVerticalSlide

Vincent Smithers 13 Apr 26, 2022