Skip to content

Commit

Permalink
OnChange for precision scrubbing
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobp100 committed Aug 9, 2023
1 parent 8a6c481 commit 7bcd07d
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 24 deletions.
42 changes: 33 additions & 9 deletions Sources/Sliders/Base/PrecisionScrubbing.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,47 @@
import SwiftUI

public struct PrecisionScrubbingConfig {
let scrubValue: (Float) -> Float
let onChange: ((Float?) -> Void)?
}

struct PrecisionScrubbingKey: EnvironmentKey {
static let defaultValue: (Float) -> Float = { _ in 1 }
typealias Value = PrecisionScrubbingConfig

static let defaultValue: PrecisionScrubbingConfig = PrecisionScrubbingConfig(
scrubValue: { _ in 1 },
onChange: nil
)
}

extension EnvironmentValues {
var precisionScrubbing: (Float) -> Float {
var precisionScrubbing: PrecisionScrubbingConfig {
get { self[PrecisionScrubbingKey.self] }
set { self[PrecisionScrubbingKey.self] = newValue }
}
}

public extension View {
func precisionScrubbing<ScrubValue: RawRepresentable>(
_ getScrubValue: @escaping (Float) -> ScrubValue
) -> some View
where ScrubValue.RawValue == Float {
self.environment(\.precisionScrubbing, { offset in
getScrubValue(offset).rawValue
})
func precisionScrubbing<ScrubValue: RawRepresentable<Float>>(
_ scrubValue: @escaping (Float) -> ScrubValue,
onChange: ((ScrubValue?) -> Void)? = nil
) -> some View {
self.environment(
\.precisionScrubbing,
PrecisionScrubbingConfig { offset in
scrubValue(offset).rawValue
} onChange: { value in
guard let value else {
onChange?(nil)
return
}

if let value = ScrubValue(rawValue: value) {
onChange?(value)
} else {
print("Invalid conversion")
}
}
)
}
}
3 changes: 3 additions & 0 deletions Sources/Sliders/Base/SliderGestureState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Foundation
import SwiftUI

public struct SliderGestureState: Equatable {
var speed: Float?
private var lastOffset: CGFloat
private var accumulations: [Float:CGFloat] = [1: 0]

Expand All @@ -19,6 +20,8 @@ public struct SliderGestureState: Equatable {
func updating(with offset: CGFloat, speed: Float) -> Self {
var mutSelf = self

mutSelf.speed = speed

var accumulations = self.accumulations.reduce([:]) { (accum: [Float:CGFloat], element) in
let (elementSpeed, elementValue) = element

Expand Down
4 changes: 2 additions & 2 deletions Sources/Sliders/RangeSlider/RangeSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ extension RangeSlider {
step: CGFloat(step),
distance: CGFloat(distance.lowerBound) ... CGFloat(distance.upperBound),
onEditingChanged: onEditingChanged,
precisionScrubbing: { _ in 1 },
precisionScrubbing: PrecisionScrubbingKey.defaultValue,
dragOffset: .constant(0),
lowerGestureState: .init(initialValue: nil),
upperGestureState: .init(initialValue: nil)
Expand All @@ -72,7 +72,7 @@ extension RangeSlider {
step: CGFloat(step),
distance: CGFloat(distance.lowerBound) ... CGFloat(distance.upperBound),
onEditingChanged: onEditingChanged,
precisionScrubbing: { _ in 1 },
precisionScrubbing: PrecisionScrubbingKey.defaultValue,
dragOffset: .constant(0),
lowerGestureState: .init(initialValue: nil),
upperGestureState: .init(initialValue: nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ public struct RangeSliderStyleConfiguration {
public let step: CGFloat
public let distance: ClosedRange<CGFloat>
public let onEditingChanged: (Bool) -> Void
public var precisionScrubbing: (Float) -> Float
public var precisionScrubbing: PrecisionScrubbingConfig
public var dragOffset: Binding<CGFloat?>
public var lowerGestureState: GestureState<SliderGestureState?>
public var upperGestureState: GestureState<SliderGestureState?>

func with(precisionScrubbing: @escaping (Float) -> Float, dragOffset: Binding<CGFloat?>, lowerGestureState: GestureState<SliderGestureState?>, upperGestureState: GestureState<SliderGestureState?>) -> Self {
func with(precisionScrubbing: PrecisionScrubbingConfig, dragOffset: Binding<CGFloat?>, lowerGestureState: GestureState<SliderGestureState?>, upperGestureState: GestureState<SliderGestureState?>) -> Self {
var mutSelf = self
mutSelf.precisionScrubbing = precisionScrubbing
mutSelf.dragOffset = dragOffset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ public struct HorizontalRangeSliderStyle<Track: View, LowerThumb: View, UpperThu
}

public func makeBody(configuration: Self.Configuration) -> some View {
GeometryReader { geometry in
let editing = configuration.lowerGestureState.wrappedValue != nil || configuration.upperGestureState.wrappedValue != nil
let precisionScrubbingSpeed = { () -> Float? in
switch (
configuration.lowerGestureState.wrappedValue?.speed,
configuration.upperGestureState.wrappedValue?.speed
) {
case (let lower?, let upper?): return min(lower, upper)
case (let only?, nil), (nil, let only?): return only
case (nil, nil): return nil
}
}()

return GeometryReader { geometry in
ZStack {
self.track
.environment(\.trackRange, configuration.range.wrappedValue)
Expand Down Expand Up @@ -68,7 +80,7 @@ public struct HorizontalRangeSliderStyle<Track: View, LowerThumb: View, UpperThu
return SliderGestureState(initialOffset: value.location.x - x)
}()).updating(
with: value.location.x,
speed: configuration.precisionScrubbing(Float(value.translation.height))
speed: configuration.precisionScrubbing.scrubValue(Float(value.translation.height))
)
},
TapGesture()
Expand Down Expand Up @@ -96,7 +108,7 @@ public struct HorizontalRangeSliderStyle<Track: View, LowerThumb: View, UpperThu
return SliderGestureState(initialOffset: value.location.x - x)
}()).updating(
with: value.location.x,
speed: configuration.precisionScrubbing(Float(value.translation.height))
speed: configuration.precisionScrubbing.scrubValue(Float(value.translation.height))
)
},
TapGesture()
Expand Down Expand Up @@ -151,6 +163,9 @@ public struct HorizontalRangeSliderStyle<Track: View, LowerThumb: View, UpperThu
.onChange(of: configuration.lowerGestureState.wrappedValue != nil || configuration.upperGestureState.wrappedValue != nil) { editing in
configuration.onEditingChanged(editing)
}
.onChange(of: precisionScrubbingSpeed) { precisionScrubbingSpeed in
configuration.precisionScrubbing.onChange?(precisionScrubbingSpeed)
}
}
.frame(minHeight: max(self.lowerThumbInteractiveSize.height, self.upperThumbInteractiveSize.height))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public struct ValueSliderStyleConfiguration {
public let bounds: ClosedRange<CGFloat>
public let step: CGFloat
public let onEditingChanged: (Bool) -> Void
public var precisionScrubbing: (Float) -> Float
public var precisionScrubbing: PrecisionScrubbingConfig
public var dragOffset: Binding<CGFloat?>
public var gestureState: GestureState<SliderGestureState?>

Expand All @@ -14,7 +14,7 @@ public struct ValueSliderStyleConfiguration {
bounds: ClosedRange<CGFloat>,
step: CGFloat,
onEditingChanged: @escaping (Bool) -> Void,
precisionScrubbing: @escaping (Float) -> Float,
precisionScrubbing: PrecisionScrubbingConfig,
dragOffset: Binding<CGFloat?>,
gestureState: GestureState<SliderGestureState?>
) {
Expand All @@ -27,7 +27,7 @@ public struct ValueSliderStyleConfiguration {
self.gestureState = gestureState
}

func with(precisionScrubbing: @escaping (Float) -> Float, dragOffset: Binding<CGFloat?>, gestureState: GestureState<SliderGestureState?>) -> Self {
func with(precisionScrubbing: PrecisionScrubbingConfig, dragOffset: Binding<CGFloat?>, gestureState: GestureState<SliderGestureState?>) -> Self {
var mutSelf = self
mutSelf.precisionScrubbing = precisionScrubbing
mutSelf.dragOffset = dragOffset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public struct HorizontalValueSliderStyle<Track: View, Thumb: View>: ValueSliderS
.updating(configuration.gestureState) { value, state, transaction in
state = (state ?? SliderGestureState(initialOffset: 0)).updating(
with: value.location.x,
speed: configuration.precisionScrubbing(Float(value.translation.height))
speed: configuration.precisionScrubbing.scrubValue(Float(value.translation.height))
)
}
)
Expand All @@ -60,7 +60,7 @@ public struct HorizontalValueSliderStyle<Track: View, Thumb: View>: ValueSliderS
return SliderGestureState(initialOffset: value.location.x - x)
}()).updating(
with: value.location.x,
speed: configuration.precisionScrubbing(Float(value.translation.height))
speed: configuration.precisionScrubbing.scrubValue(Float(value.translation.height))
)
}
)
Expand All @@ -81,6 +81,9 @@ public struct HorizontalValueSliderStyle<Track: View, Thumb: View>: ValueSliderS
.onChange(of: configuration.gestureState.wrappedValue != nil) { editing in
configuration.onEditingChanged(editing)
}
.onChange(of: configuration.gestureState.wrappedValue?.speed) { speed in
configuration.precisionScrubbing.onChange?(speed)
}
}
.frame(minHeight: self.thumbInteractiveSize.height)
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/Sliders/ValueSlider/ValueSlider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ extension ValueSlider {
bounds: CGFloat(bounds.lowerBound)...CGFloat(bounds.upperBound),
step: CGFloat(step),
onEditingChanged: onEditingChanged,
precisionScrubbing: { _ in 1 },
precisionScrubbing: PrecisionScrubbingKey.defaultValue,
dragOffset: .constant(0),
gestureState: .init(initialValue: nil)
)
Expand All @@ -59,7 +59,7 @@ extension ValueSlider {
bounds: CGFloat(bounds.lowerBound)...CGFloat(bounds.upperBound),
step: CGFloat(step),
onEditingChanged: onEditingChanged,
precisionScrubbing: { _ in 1 },
precisionScrubbing: PrecisionScrubbingKey.defaultValue,
dragOffset: .constant(0),
gestureState: .init(initialValue: nil)
)
Expand All @@ -85,7 +85,7 @@ extension ValueSlider {
bounds: CGFloat(bounds.lowerBound.value)...CGFloat(bounds.upperBound.value),
step: CGFloat(step.value),
onEditingChanged: onEditingChanged,
precisionScrubbing: { _ in 1 },
precisionScrubbing: PrecisionScrubbingKey.defaultValue,
dragOffset: .constant(0),
gestureState: .init(initialValue: nil)
)
Expand Down

0 comments on commit 7bcd07d

Please sign in to comment.