I am looking at creating a custom slider, with a gradient background, numbers to represent the values, and white dots on the line to show where each step is, what I'm looking for looks like this:
I have looked at several other tutorials but none of what I do gives me the result Im after, either because elements don't line up, or my code doesn't work as well as the default slider.
An extra bit of trickiness is I also want to have the snap to position like the default slider, so a user can't select a value of 6.5522, only 6 or seven.
Here is the code I currently have:
import SwiftUI
struct CustomSlider: View {
let textColor: Color
let thumbColor: Color
let height: CGFloat
let cornerRadius: CGFloat
@State var lastOffset: CGFloat = 0
@Binding var value: CGFloat
var range: ClosedRange<CGFloat>
var leadingOffset: CGFloat = 5
var trailingOffset: CGFloat = 5
var body: some View {
GeometryReader { geo in
VStack {
ZStack {
RoundedRectangle(cornerRadius: cornerRadius).fill(LinearGradient(gradient: Gradient(colors: [.paleRed, .mango, .neonYellow, .midGreen]), startPoint: .leading, endPoint: .trailing))
.frame(height: height)
HStack {
ForEach(1..<11) { index in
Circle()
.foregroundColor(.white)
.frame(width: height, height: height)
if index < 10 {
Spacer()
}
}
}
HStack {
Circle()
.frame(width: height * 2, height: height * 2)
.foregroundColor(thumbColor)
.offset(x: CGFloat(self.$value.wrappedValue.map(from: self.range, to: 6...(geo.size.width - 6 - 22))))
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
if abs(value.translation.width) < 0.1 {
self.lastOffset = self.$value.wrappedValue.map(from: self.range, to: self.leadingOffset...(geo.size.width - (height * 2) - self.trailingOffset))
}
let sliderPos = max(0 + self.leadingOffset, min(self.lastOffset + value.translation.width, geo.size.width - (height * 2) - self.trailingOffset))
let sliderVal = sliderPos.map(from: self.leadingOffset...(geo.size.width - (height * 2) - self.trailingOffset), to: self.range)
self.value = sliderVal
}
)
Spacer()
}
}
HStack {
ForEach(1..<11) { index in
Text("\(index)")
.foregroundColor(.secondaryButtonLightGrey)
.font(.custom("Rubik-Medium", size: 16))
if index < 10 {
Spacer()
}
}
}
}
}
}
}
// How its implemented in the view code:
CustomSlider(textColor: .textColor, thumbColor: .thumbColor, height: 10, cornerRadius: 10, value: $value, range: 0...10)
I apologize if these are really simple questions but I am still new to swiftUI, only been using it for a month.
Thank you for any advice.
