Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm trying to create a UISlider that lets you choose from an array of numbers. Each slider position should be equidistant and the slider should snap to each position, rather than smoothly slide between them. (This is the behavior of the slider in Settings > General > Text Size, which was introduced in iOS 7.)

iOS 7 Text Size Slider

The numbers I want to choose from are: -3, 0, 2, 4, 7, 10, and 12.

(I'm very new to Objective-C, so a complete code example would be much more helpful than a code snippet. =)

like image 268
Matias Vad Avatar asked Nov 21 '11 21:11

Matias Vad


1 Answers

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)         }     } } 
like image 167
matsr Avatar answered Oct 02 '22 22:10

matsr