Sliders-SwiftUI

repository·master·Indexed 20 days ago

https://github.com/kieranb662/sliders-swiftui

A collection of highly stylable, drag-based SwiftUI controls designed to extend the standard SwiftUI toolkit. The library includes linear (LSlider), circular (RSlider), 2-D (TrackPad, RadialPad, Joystick), path-based (PSlider), and range-based sliders (DoubleLSlider, DoubleRSlider), as well as an OverflowSlider. Key features include full styling via protocols, spatial adaptability with angle parameters, haptic feedback, and magnetic affinity snapping to tick marks.

Tokens
14K
Snippets
42
Records
61
Agent score
71%

What's inside Sliders

  1. Overview of Sliders controls

    master

    The Sliders framework provides a collection of fully stylable, drag-based SwiftUI controls designed to replace or extend the standard SwiftUI Slider.

    Key features include:

    • Full Styling: Every component uses a style protocol, allowing independent customization of the thumb, track, tick marks, and floating labels.
    • Spatial Adaptability: Controls scale to their containers and accept an angle parameter for horizontal, vertical, or diagonal placement.
    • Tick Marks & Haptics: Supports evenly-spaced, step-spaced, or arbitrary tick marks with iOS haptic feedback.
    • Magnetic Affinity Snapping: An optional behavior where the thumb snaps to nearby tick marks and requires a deliberate extra drag to break free.
  2. Overview of available Sliders controls

    master

    The library provides a variety of specialized drag-based controls:

    • LSlider: A linear slider that works at any angle.
    • DoubleLSlider: A linear range slider with two thumbs.
    • RSlider: A circular slider with a thumb traveling around an arc.
    • DoubleRSlider: A circular range slider with two thumbs.
    • TrackPad: A 2-D slider mapping horizontal/vertical drag to two values.
    • RadialPad: A joystick-style 2-D control that retains position.
    • Joystick: An on-screen joystick that appears at the drag location.
    • PSlider: A slider where the thumb travels along a custom SwiftUI Shape path.
    • OverflowSlider: A meter-style slider with velocity gestures.
  3. Understand JoyState and its properties

    master

    JoyState is an enumeration representing the joystick's lifecycle:

    • .inactive: The joystick is hidden.
    • .locked: The joystick is locked in place after a drag ended inside the lock-box.
    • .dragging(time:translation:startLocation:velocity:acceleration:): The user is actively dragging.

    Computed Properties

    You can access the following properties on a JoyState value to drive your application logic:

    • isActive: true when dragging or locked.
    • isLocked: true when locked.
    • isDragging: true when actively dragging.
    • angle: The Angle from the center to the thumb.
    • radialOffset: The Double distance from the center to the thumb.
    • translation: The current CGSize drag translation.
    • velocity: The current CGSize drag velocity.
    • acceleration: The current CGSize drag acceleration.
  4. Use RadialPad for 2-D joystick-style control

    master

    RadialPad is a 2-D control where a thumb moves within a circular track. Unlike a standard joystick, the thumb remains at its last dragged position when the gesture ends. It tracks a normalized radial distance (offset, 0…1) and an Angle direction.

    Core Features

    • Radial Distance & Angle: Controlled via offset and angle bindings.
    • Previous-value indicator: A ghost marker showing the last committed position. Supports snapping when dragging slowly back to that position.
    • Polar Tick Marks: Concentric rings (r-ticks) and angular spokes (θ-ticks) that act as snap points.
    • Single-tap select: Allows users to tap the track to immediately place the thumb.
    • Haptic feedback: Built-in feedback for edge contact, previous-value snapping, and tick snapping.
    • Custom Styling: Fully customizable via the RadialPadStyle protocol.
    @State var dist = 0.0
    @State var dir  = Angle.zero
    
    RadialPad(offset: $dist, angle: $dir)
        .frame(width: 260, height: 260)
  5. Enable Tick Affinity (Snapping)

    master

    When affinityEnabled is set to true and tickMarkSpacing is configured, thumbs will magnetically snap to the nearest tick mark when they enter the affinityRadius.

    To break free from a snap, the user must drag the thumb beyond the affinityRadius plus the affinityResistance distance.

    DoubleLSlider(
        lowerValue: $lower,
        upperValue: $upper,
        range: 0...1,
        keepThumbInTrack: true,
        trackThickness: 20,
        tickMarkSpacing: .count(11),
        affinityEnabled: true,
        affinityRadius: 0.03,
        affinityResistance: 0.015
    )
    .frame(height: 60)
  6. Style the TrackPad using TrackPadStyle

    master

    You can fully customize the appearance of the TrackPad by conforming to the TrackPadStyle protocol or using the built-in .default style with custom parameters.

    Using the Default Style with Custom Parameters

    Use .trackPadStyle(_:) to pass specific colors and sizes to the default implementation:

    TrackPad($point)
        .trackPadStyle(
            .default(
                trackColor: Color.indigo.opacity(0.15),
                trackStrokeColor: Color.indigo,
                thumbInactiveColor: Color.indigo,
                thumbActiveColor: Color.white,
                thumbSize: 44
            )
        )
        .frame(height: 260)

    Custom Implementation

    To create a completely custom look, implement the TrackPadStyle protocol. You must provide implementations for:

    • makeThumb(configuration:) — The draggable thumb.
    • makeTrack(configuration:) — The rectangular track background.

    Optional methods include:

    • makePreviousValueIndicator(configuration:)
    • makeTickMarks(configuration:)
    • makeLabel(configuration:content:)

    TrackPadConfiguration is passed to these methods and contains the current state, including position, ranges, and snap states.

    TrackPad($point)
        .trackPadStyle(
            .default(
                trackColor: Color.indigo.opacity(0.15),
                trackStrokeColor: Color.indigo,
                thumbInactiveColor: Color.indigo,
                thumbActiveColor: Color.white,
                thumbSize: 44
            )
        )
        .frame(height: 260)
  7. Customize TrackPad labels

    master

    TrackPad displays a floating label above the thumb that updates live. You can provide a custom view builder to control the label's appearance.

    Custom Label View

    Use a trailing closure to define the label content, receiving the current x and y values:

    @State var point = CGPoint(x: 0.5, y: 0.5)
    
    TrackPad($point, rangeX: -1...1, rangeY: -1...1) {\
        Text(String(format: "x: %.2f  y: %.2f", x, y))
    }
    .frame(height: 260)

    Hiding Labels

    Use the .labelsVisibility(_:) modifier to hide the label entirely:

    TrackPad($point)
        .labelsVisibility(.hidden)
        .frame(height: 260)
    TrackPad($point, rangeX: -1...1, rangeY: -1...1) {
        Text(String(format: "x: %.2f  y: %.2f", x, y))
    }
    .frame(height: 260)
  8. Install Sliders via Swift Package Manager

    master

    You can integrate Sliders into your Xcode project using Swift Package Manager (SPM).

    Via Xcode UI

    1. Go to File → Add Package Dependencies.
    2. Paste the repository URL: https://github.com/kieranb662/Sliders-SwiftUI.
    3. Select your minimum version requirement and add the package to your target.

    Via Package.swift

    Add the dependency directly to your Package.swift file:

    dependencies: [
        .package(url: "https://github.com/kieranb662/Sliders-SwiftUI", from: "1.0.0")
    ]
    dependencies: [
        .package(url: "https://github.com/kieranb662/Sliders-SwiftUI", from: "1.0.0")
    ]
  9. Apply custom styles to Sliders

    master

    All controls follow a consistent styling pattern: implement a style protocol and apply it using a specific modifier. Style modifiers cascade through the view hierarchy, meaning applying a modifier to a container will style all compatible sliders within that container.

    // Apply a custom style to every LSlider in a container
    VStack {
        LSlider($red,   range: 0...1)
        LSlider($green, range: 0...1)
        LSlider($blue,  range: 0...1)
    }
    .linearSliderStyle(MyLSliderStyle())
  10. Customizing PSlider with PSliderStyle

    master

    To change the appearance of the thumb and the track, conform to PSliderStyle and implement two required methods. Apply the style using the .pathSliderStyle(_:) modifier.

    • makeThumb(configuration:): Returns the view for the draggable thumb.
    • makeTrack(configuration:): Returns the view for the track that fills/empties as the thumb moves.

    Use the provided PSliderConfiguration within these methods to access state like isActive, pctFill, and the shape itself.

    struct MyPSliderStyle: PSliderStyle {
        func makeThumb(configuration: PSliderConfiguration) -> some View {
            Circle()
                .frame(width: 30, height: 30)
                .foregroundColor(configuration.isActive ? Color.yellow : Color.white)
        }
    
        func makeTrack(configuration: PSliderConfiguration) -> some View {
            ZStack {
                configuration.shape
                    .stroke(Color.gray, lineWidth: 8)
                configuration.shape
                    .trim(from: 0, to: CGFloat(configuration.pctFill))
                    .stroke(Color.purple, lineWidth: 10)
            }
        }
    }
    
    // Usage
    PSlider($value, shape: Circle())
        .pathSliderStyle(MyPSliderStyle())
        .frame(width: 200, height: 200)