iosswiftswiftui

How to create slider with tick marks using SwiftUI?


Using SwiftUI, I would like to create a slider with a min/max range of 5-20 where the user can slide and go up by 5. Slider out of the box looks very 'flat'/smooth. Is there a way I can add said tickmarks so that it looks like the slider is 'snapping' to the ticks?

If the slider is considered not to be the right way to do that effect, am open to other options.


Solution

  • The Slider has a bunch of initializer options, some of which include a 'step' parameter, which creates the snapping effect. It doesn't have visible tick marks along the edge, but you could create that manually alongside.

    import SwiftUI
    
    struct SliderView: View {
        
        @State var value: CGFloat = 10
        let min: CGFloat = 5
        let max: CGFloat = 20
        let step: CGFloat = 5
        
        var body: some View {
            VStack {
                Text(formated(value: value))
                
                Slider(
                    value: $value,
                    in: min...max,
                    step: step,
                    minimumValueLabel: Text(formated(value: min)),
                    maximumValueLabel: Text(formated(value: max)),
                    label: { })
    
                VStack(spacing: 0) {
                    Slider(
                        value: $value,
                        in: min...max,
                        step: step)
                        .accentColor(.red)
                    
                    HStack(spacing: 0) {
                        let count: Int = Int((max / step))
                        ForEach(0..<count) { index in
                            VStack {
                                Text("I")
                                Text("\(index * Int(step) + Int(min))")
                            }
                            .offset(x:
                              index == 1 ? 5 :
                              index == 2 ? 4 :
                              0
                            )
                            if index != (count - 1) {
                                Spacer()
                            }
                        }
                    }
                    .padding(.horizontal, 8)
                }
                .padding()
            }
            .padding()
        }
        
        func formated(value: CGFloat) -> String {
            return String(format: "%.0f", value)
        }
        
    }