49

I am trying to implement some form of snapping or steps with the UISlider. I have written the following code but it does not work as smooth as I hoped for. It works, but when the I slide it upwards it snap 5points to the right leaving the finger not centered over the "slide-circle"

This is my code where self.lastQuestionSliderValue is a property of the class which I have set to the initial value of the slider.

    if (self.questionSlider.value > self.lastQuestionSliderValue) {
        self.questionSlider.value += 5.0;
    } else {
        self.questionSlider.value -= 5.0;
    }

    self.lastQuestionSliderValue = (int)self.questionSlider.value;
LuckyLuke
  • 42,935
  • 77
  • 254
  • 416

6 Answers6

137

It's actually considerably easier than I first thought. Originally I was trying to get the thumbrect property and do complicated math. Here's what I ended up with:

h File:

@property (nonatomic, retain) IBOutlet UISlider* questionSlider;
@property (nonatomic) float lastQuestionStep;
@property (nonatomic) float stepValue;

m File:

- (void)viewDidLoad {
    [super viewDidLoad];

    // Set the step to whatever you want. Make sure the step value makes sense
    //   when compared to the min/max values for the slider. You could take this
    //   example a step further and instead use a variable for the number of
    //   steps you wanted.
    self.stepValue = 25.0f;

    // Set the initial value to prevent any weird inconsistencies.
    self.lastQuestionStep = (self.questionSlider.value) / self.stepValue;
}

// This is the "valueChanged" method for the UISlider. Hook this up in
//   Interface Builder.
-(IBAction)valueChanged:(id)sender {
    // This determines which "step" the slider should be on. Here we're taking 
    //   the current position of the slider and dividing by the `self.stepValue`
    //   to determine approximately which step we are on. Then we round to get to
    //   find which step we are closest to.
    float newStep = roundf((questionSlider.value) / self.stepValue);

    // Convert "steps" back to the context of the sliders values.
    self.questionSlider.value = newStep * self.stepValue;
}

Make sure you hook up the method and the outlet for your UISlider view and you should be good to go.

Bohdan Ivanov
  • 689
  • 8
  • 22
FreeAsInBeer
  • 12,837
  • 5
  • 46
  • 79
26

The simplest solution to me was just

- (IBAction)sliderValueChanged:(id)sender {
    UISlider *slider = sender;
    slider.value = roundf(slider.value);
}
Adam Johns
  • 32,040
  • 21
  • 108
  • 161
8

Maybe someone will need! In my situation I needed any integer step, so I used the following code:

-(void)valueChanged:(id)sender {
    UISlider *slider = sender;
    slider.value = (int)slider.value;
}
rusBogun
  • 389
  • 3
  • 11
  • 1
    The issue I see with this is that it won't lock into an int until you slide all the way to that int value. For example if you have a slider from 1 to 2, if the slider slides anywhere from 1 to 1.99 it will stay on 1. Then when you slide to 2 it will slide to 2. Because int rounds everything down. See my answer for a better rounding approach. – Adam Johns Dec 05 '14 at 15:25
7

SWIFT VERSION

Example: You want a slider to go from 1-10000 in steps of 100. UISlider setup is as follows:

slider.maximumValue = 100
slider.minimumValue = 0
slider.continuous = true

In the action func() for the slider use:

var sliderValue:Int = Int(sender.value) * 100
Pescolly
  • 852
  • 11
  • 17
4

A really simple one:

- (void)sliderUpdated:(UISlider*)sli {
    CGFloat steps = 5;
    sli.value = roundf(sli.value/sli.maximumValue*steps)*sli.maximumValue/steps;    
}

Great if you want a fast solution and you've added the target by UIControlEventValueChanged.

Leonard Pauli
  • 2,466
  • 1
  • 19
  • 21
4

Another Swift approach is to do something like

let step: Float = 10
@IBAction func sliderValueChanged(sender: UISlider) {
  let roundedValue = round(sender.value / step) * step
  sender.value = roundedValue
  // Do something else with the value

}
Jean-François Fabre
  • 126,787
  • 22
  • 103
  • 165
Jure
  • 2,783
  • 3
  • 21
  • 26
  • Do you mean `round(sender.value * step) / step` ? – Aneel Nov 24 '15 at 06:17
  • The formula I use works like this: Lets say the UISlider goes from 0 - 100 and you want steps of 10. If the current slider value is 14, your slider should "snap" to 10: `round(14/10) * 10 = 1 * 10 = 10` If your slider has the value 46, you want it to snap to 50: `round(46/10) * 10 = 5 * 10 = 50` In your case, it would produce the same result: `round(46*10) / 10 = 460 / 10 = 46` Hope this makes it clear. :) – Jure Nov 24 '15 at 08:27
  • That makes sense. I was using a slider with the default minimum and maximum values (0..1), and this formula always produced 0. In that case, multiplying before dividing will produce 0.0, 0.1, 0.2,... 0.9, 1.0. But it's probably a more common case to set a different maximum. – Aneel Nov 24 '15 at 13:54
  • Oh got it! Yes, if you can, set a higher min and max, otherwise you'll have to do some more calculations, like you figured out. – Jure Nov 24 '15 at 14:55
  • I suggest change `round()` to `floor()` to avoid sliding jitter – Johnny Oct 24 '17 at 03:18