UISlider that snaps to a fixed number of steps (like Text Size in the iOS 7 Settings app) UISlider that snaps to a fixed number of steps (like Text Size in the iOS 7 Settings app) ios ios

UISlider that snaps to a fixed number of steps (like Text Size in the iOS 7 Settings app)


Some of the other answers work, but this will give you the same fixed space between every position in your slider. In this example you treat the slider positions as indexes to an array which contains the actual numeric values you are interested in.

@interface MyViewController : UIViewController {    UISlider *slider;    NSArray *numbers;}@end@implementation MyViewController- (void)viewDidLoad {    [super viewDidLoad];    slider = [[UISlider alloc] initWithFrame:self.view.bounds];    [self.view addSubview:slider];    // These number values represent each slider position    numbers = @[@(-3), @(0), @(2), @(4), @(7), @(10), @(12)];    // slider values go from 0 to the number of values in your numbers array    NSInteger numberOfSteps = ((float)[numbers count] - 1);    slider.maximumValue = numberOfSteps;    slider.minimumValue = 0;    // As the slider moves it will continously call the -valueChanged:     slider.continuous = YES; // NO makes it call only once you let go    [slider addTarget:self               action:@selector(valueChanged:)     forControlEvents:UIControlEventValueChanged];}- (void)valueChanged:(UISlider *)sender {    // round the slider position to the nearest index of the numbers array    NSUInteger index = (NSUInteger)(slider.value + 0.5);    [slider setValue:index animated:NO];    NSNumber *number = numbers[index]; // <-- This numeric value you want    NSLog(@"sliderIndex: %i", (int)index);    NSLog(@"number: %@", number);}

I hope that helps, good luck.

Edit: Here's a version in Swift 4 that subclasses UISlider with callbacks.

class MySliderStepper: UISlider {    private let values: [Float]    private var lastIndex: Int? = nil    let callback: (Float) -> Void    init(frame: CGRect, values: [Float], callback: @escaping (_ newValue: Float) -> Void) {        self.values = values        self.callback = callback        super.init(frame: frame)        self.addTarget(self, action: #selector(handleValueChange(sender:)), for: .valueChanged)        let steps = values.count - 1        self.minimumValue = 0        self.maximumValue = Float(steps)    }    required init?(coder aDecoder: NSCoder) {        fatalError("init(coder:) has not been implemented")    }    @objc func handleValueChange(sender: UISlider) {        let newIndex = Int(sender.value + 0.5) // round up to next index        self.setValue(Float(newIndex), animated: false) // snap to increments        let didChange = lastIndex == nil || newIndex != lastIndex!        if didChange {            lastIndex = newIndex            let actualValue = self.values[newIndex]            self.callback(actualValue)        }    }}


If you have regular spaces between steps, you can use it like this for 15 value:

@IBAction func timeSliderChanged(sender: UISlider) {    let newValue = Int(sender.value/15) * 15    sender.setValue(Float(newValue), animated: false)}


This worked for my particular case, using @IBDesignable:

Requires even, integer intervals:

@IBDesignableclass SnappableSlider: UISlider {    @IBInspectable    var interval: Int = 1    override init(frame: CGRect) {        super.init(frame: frame)        setUpSlider()    }    required init?(coder aDecoder: NSCoder) {        super.init(coder: aDecoder)        setUpSlider()    }    private func setUpSlider() {        addTarget(self, action: #selector(handleValueChange(sender:)), for: .valueChanged)    }    @objc func handleValueChange(sender: UISlider) {        let newValue =  (sender.value / Float(interval)).rounded() * Float(interval)        setValue(Float(newValue), animated: false)    }}