web-dev-qa-db-fra.com

UISlider qui s'accroche à un nombre fixe d'étapes (comme la taille du texte dans l'application Paramètres iOS 7)

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.)

iOS 7 Text Size Slider

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. =)

50
Matias Vad

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.

Edit: Voici une version en Swift 4 qui sous-classe UISlider avec des rappels.

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)
        }
    }
}
119
matsr

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)
}
5

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];
}
2
PengOne

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)
    }
}
0
taykay08

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];
}
0
thedonniew