Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting the value of a SwiftUI slider by tapping on it

Tags:

swiftui

slider

How can I change the value of a SwiftUI Slider by tapping on it?

like image 949
bjrnt Avatar asked Dec 09 '25 13:12

bjrnt


2 Answers

Here's a minimal implementation I created using a GeometryReader that rounds to the closest value:

struct TappableSlider: View {
    var value: Binding<Float>
    var range: ClosedRange<Float>
    var step: Float

    var body: some View {
        GeometryReader { geometry in
            Slider(value: self.value, in: self.range, step: self.step)
                .gesture(DragGesture(minimumDistance: 0).onEnded { value in
                    let percent = min(max(0, Float(value.location.x / geometry.size.width * 1)), 1)
                    let newValue = self.range.lowerBound + round(percent * (self.range.upperBound - self.range.lowerBound))
                    self.value.wrappedValue = newValue
                })
        }
    }
}

which can be used just like a normal slider, e.g.

TappableSlider(value: $sliderValue, in: 1...7, step: 1.0)
like image 132
bjrnt Avatar answered Dec 12 '25 12:12

bjrnt


Here is a fully correct implementation that takes into account that you can have different step values and lowerBound might be greater than 0.

The original soultion doesn't take the step into account. Also the frame is used to avoid the view shrinking.

struct TappableSlider: View {
    var value: Binding<Double>
    var range: ClosedRange<Double>
    var step: Double

    init(value: Binding<Double>, in range: ClosedRange<Double>, step: Double) {
        self.value = value
        self.range = range
        self.step = step
    }

    var body: some View {
        GeometryReader { geometry in
            Slider(value: value, in: range, step: step)
                .gesture(DragGesture(minimumDistance: 0).onChanged { gestureValue in
                    // Ensure the drag location is within the view's bounds and the step is not zero
                    guard gestureValue.location.x >= 0 && gestureValue.location.x <= geometry.size.width, step != 0 else { return }

                    // Calculate the percentage of the slider's width where the drag happened
                    let clampedPercent = max(0, min(gestureValue.location.x / geometry.size.width, 1))

                    // Scale this percentage to the slider's value range
                    let scaledRangeValue = range.lowerBound + (clampedPercent * (range.upperBound - range.lowerBound))

                    // Round this value to the nearest step
                    let roundedToStep = round(scaledRangeValue / step) * step

                    // Ensure the final value adheres to the slider's bounds
                    let clampedValue = max(range.lowerBound, min(roundedToStep, range.upperBound))

                    // Update the bound value
                    self.value.wrappedValue = clampedValue
                })
                .frame(
                    width: geometry.frame(in: .global).width,
                    height: geometry.frame(in: .global).height
                )
        }
    }
}
like image 23
Anton Kondrashov Avatar answered Dec 12 '25 12:12

Anton Kondrashov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!