J'essaie de créer un UISlider
qui vous permet de choisir parmi un tableau de nombres. Chaque position du curseur doit être équidistante et le curseur doit s'accrocher à chaque position, plutôt que de glisser doucement entre eux. (C'est le comportement du curseur dans Settings
> General
> Text Size
, qui a été introduit dans iOS 7.)
Les numéros que je veux choisir sont: -3, 0, 2, 4, 7, 10, and 12.
(Je suis très nouveau sur Objective-C, donc un exemple de code complet serait beaucoup plus utile qu'un extrait de code. =)
Certaines des autres réponses fonctionnent, mais cela vous donnera le même espace fixe entre chaque position de votre curseur. Dans cet exemple, vous traitez les positions des curseurs comme des index d'un tableau qui contient les valeurs numériques réelles qui vous intéressent.
@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);
}
J'espère que ça aide, bonne chance.
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 {
let actualValue = self.values[newIndex]
self.callback(actualValue)
}
}
}
Si vous avez des espaces réguliers entre les étapes, vous pouvez l'utiliser comme ceci pour 15
valeur:
@IBAction func timeSliderChanged(sender: UISlider) {
let newValue = Int(sender.value/15) * 15
sender.setValue(Float(newValue), animated: false)
}
La réponse est essentiellement la même à cette réponse à ISlider avec des incréments de 5
Pour le modifier pour qu'il fonctionne dans votre cas, vous devrez créer une fonction d'arrondi qui renvoie uniquement les valeurs souhaitées. Par exemple, vous pouvez faire quelque chose de simple (bien que hacky) comme ceci:
-(int)roundSliderValue:(float)x {
if (x < -1.5) {
return -3;
} else if (x < 1.0) {
return 0;
} else if (x < 3.0) {
return 2;
} else if (x < 5.5) {
return 4;
} else if (x < 8.5) {
return 7;
} else if (x < 11.0) {
return 10;
} else {
return 12;
}
}
Utilisez maintenant la réponse du post précédent pour arrondir la valeur.
slider.continuous = YES;
[slider addTarget:self
action:@selector(valueChanged:)
forControlEvents:UIControlEventValueChanged];
Enfin, implémentez les modifications:
-(void)valueChanged:(id)slider {
[slider setValue:[self roundSliderValue:slider.value] animated:NO];
}
Cela a fonctionné pour mon cas particulier, en utilisant @IBDesignable:
Nécessite des intervalles pairs et entiers:
@IBDesignable
class 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)
}
}
Approche similaire à PengOne, mais j'ai fait un arrondi manuel pour clarifier ce qui se passait.
- (IBAction)sliderDidEndEditing:(UISlider *)slider {
// default value slider will start at
float newValue = 0.0;
if (slider.value < -1.5) {
newValue = -3;
} else if (slider.value < 1) {
newValue = 0;
} else if (slider.value < 3) {
newValue = 2;
} else if (slider.value < 5.5) {
newValue = 4;
} else if (slider.value < 8.5) {
newValue = 7;
} else if (slider.value < 11) {
newValue = 10;
} else {
newValue = 12;
}
slider.value = newValue;
}
- (IBAction)sliderValueChanged:(id)sender {
UISlider *slider = sender;
float newValue = 0.0;
if (slider.value < -1.5) {
newValue = -3;
} else if (slider.value < 1) {
newValue = 0;
} else if (slider.value < 3) {
newValue = 2;
} else if (slider.value < 5.5) {
newValue = 4;
} else if (slider.value < 8.5) {
newValue = 7;
} else if (slider.value < 11) {
newValue = 10;
} else {
newValue = 12;
}
// and if you have a label displaying the slider value, set it
[yourLabel].text = [NSString stringWithFormat:@"%d", (int)newValue];
}