Question

J'ai un contrôle segmenté dans lequel l'utilisateur peut choisir comment commander une liste. Fonctionne bien.

Cependant, j'aimerais que, lorsqu'un segment déjà sélectionné soit exploité, l'ordre soit inversé. J'ai tout le code en place, mais je ne sais pas comment enregistrer les taps sur ces segments. Il semble que le seul événement de contrôle que vous puissiez utiliser soit UIControlEventValueChanged, mais cela ne fonctionne pas (car le segment sélectionné ne change pas réellement).

Existe-t-il une solution pour cela? Et si oui, de quoi s'agit-il?

Merci d'avance!

Était-ce utile?

La solution

Vous pouvez sous-classe UISegmentedControl , puis substituer setSelectedSegmentIndex:

- (void) setSelectedSegmentIndex:(NSInteger)toValue {
    if (self.selectedSegmentIndex == toValue) {
        [super setSelectedSegmentIndex:UISegmentedControlNoSegment];
    } else {
        [super setSelectedSegmentIndex:toValue];        
    }
}

Si vous utilisez IB, assurez-vous de définir la classe de votre UISegmentedControl dans votre sous-classe.

Vous pouvez désormais écouter le même UIControlEventValueChanged que vous le feriez normalement, sauf si l'utilisateur désélectionne le segment, un selectedSegmentIndex égal à UISegmentedControlNoSegment :

-(IBAction) valueChanged: (id) sender {
    UISegmentedControl *segmentedControl = (UISegmentedControl*) sender;
    switch ([segmentedControl selectedSegmentIndex]) {
        case 0:
            // do something
            break;
        case 1:
            // do something
            break;
        case UISegmentedControlNoSegment:
            // do something
            break;
        default:
            NSLog(@"No option for: %d", [segmentedControl selectedSegmentIndex]);
    }
}

Autres conseils

Je pense que c'est encore mieux si vous utilisez - (void) touchesBegan: (NSSet *) touche withEvent: (UIEvent *) event --- car il s'agit du comportement de UISegmentedControl. En outre, il semble que vous n’ayez pas besoin de surcharger le - (void) setSelectedSegmentIndex: (NSInteger) toValue

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    
    NSInteger current = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
    if (current == self.selectedSegmentIndex) 
        [self sendActionsForControlEvents:UIControlEventValueChanged];
}

Je voulais cela moi-même. A pris la solution de Julian (merci!) Et modifié légèrement.

La sous-classe UISegmentedControl suivante déclenche simplement l'événement UIControlEventValueChanged même lorsque la valeur n'a pas changé, ce qui lit évidemment un peu bizarre, mais fonctionne correctement dans mon cas et garde les choses simples.

AKSegmentedControl.h

#import <UIKit/UIKit.h>

@interface AKSegmentedControl : UISegmentedControl {
}

@end

AKSegmentedControl.m

#import "AKSegmentedControl.h"

@implementation AKSegmentedControl

- (void)setSelectedSegmentIndex:(NSInteger)toValue {
  // Trigger UIControlEventValueChanged even when re-tapping the selected segment.
  if (toValue==self.selectedSegmentIndex) {
    [self sendActionsForControlEvents:UIControlEventValueChanged];
  }
  [super setSelectedSegmentIndex:toValue];        
}

@end

Ceci fonctionne à la fois sur iOS 4 et 5:

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  int oldValue = self.selectedSegmentIndex;
  [super touchesBegan:touches withEvent:event];
  if ( oldValue == self.selectedSegmentIndex )
    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

La solution actuelle présentée ne fonctionne toujours pas, car setSelectedSegmentIndex n'est jamais appelé à moins qu'un nouveau segment ne soit exploité. Au moins dans mon cas, cela n'a jamais fonctionné. J'utilise iOS5, cependant, cette solution a peut-être fonctionné sous iOS4. Quoi qu'il en soit, c'est ma solution. Elle nécessite une méthode de sous-classe supplémentaire, qui est la suivante:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self setSelectedSegmentIndex:self.selectedSegmentIndex];
    [super touchesEnded:touches withEvent:event];
}

Bonne chance!

C’était une question assez ancienne et j’ai donc pensé ajouter la solution assez simple que j’avais utilisée lorsque j’ai rencontré ce problème dans une application iOS 5+.

Je n’ai fait que glisser une reconnaissance de gestes tactiles sur UISegmentedControl dans mon fichier .xib. Vérifiez les connexions de votre nouveau programme de reconnaissance de mouvements pour vous assurer que le contrôle segmenté est la seule sortie de gestureRecognizer, puis connectez simplement son action à la méthode que vous souhaitez déclencher dans votre contrôleur.

Cette méthode devrait se déclencher chaque fois que vous touchez le contrôle segmenté. Il vous suffit donc de vérifier l'index sélectionné avec peut-être une variable d'instance pour voir si l'utilisateur a touché le segment déjà sélectionné.

@implementation MyViewController

@synthesize selectedSegment;
@synthesize segmentedControl;

// connected to Tap Gesture Recognizer's action
- (IBAction)segmentedControlTouched:(id)sender
{
    NSLog(@"Segmented Control Touched");
    if (selectedSegment == [segmentedControl selectedSegmentIndex]) {
        // Do some cool stuff
    }

    selectedSegment = [segmentedControl selectedSegmentIndex];

}

@end

Sur iOS8, chaque réponse semble ne pas fonctionner ou déclencher un changement deux fois. Je suis venu avec une solution très simple - donc je voudrais le partager.

Dans la sous-classe, je n'ai que:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [super touchesBegan:touches withEvent:event];
    [self setSelectedSegmentIndex:UISegmentedControlNoSegment];
}

Cela revient à réinitialiser le segment sélectionné sur -1 avant de changer le segment de contrôle segmenté. Cela se produira dans la méthode touchesEnded.

Ceci fonctionne:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    int oldValue = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
    if (oldValue == self.selectedSegmentIndex)
    {
        [super setSelectedSegmentIndex:UISegmentedControlNoSegment];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

Dans Swift 3 , je pouvais imaginer au moins deux solutions comme suit. Pour un cas particulier, j’ai également posté une troisième solution, dans laquelle le segment sélectionné fonctionne comme un bouton bascule.

Solution 1:

Astuce: cette solution ne fonctionne que sur les segments controlSegments momentanés! Si vous avez besoin d’une solution pour les commandes fixes, choisissez la solution 2

L'idée est d'enregistrer un deuxième événement qui se déclenche lors d'une retouche à l'intérieur:

// this solution works only for momentary segment control:
class Solution1ViewController : UIViewController {
    var current:Int = UISegmentedControlNoSegment
    @IBOutlet weak var mySegmentedControl: UISegmentedControl! {
        didSet {
            guard mySegmentedControl != nil else { return }

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .valueChanged)

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .touchUpInside)
        }
    }

    func changeSelection(sender: UISegmentedControl) {
        if current == sender.selectedSegmentIndex {
            // user hit the same button again!
            print("user hit the same button again!")
        }
        else {
            current = sender.selectedSegmentIndex
            // user selected a new index
             print("user selected a new index")
        }
    }
}

Solution 2: L'autre méthode consiste à remplacer les fonctions tactiles dans UISegmentedControl et à activer le valueChanged même si l'index de segment n'a pas changé. Par conséquent, vous pouvez remplacer le UISegmentedControl comme suit:

// override UISegmentControl to fire event on second hit:
class Solution2SegmentControl : UISegmentedControl
{
    private var current:Int = UISegmentedControlNoSegment
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if current == self.selectedSegmentIndex {
            self.sendActions(for: .valueChanged)
        }
    }
}

// solution2 works with stationary (default) segment controls
class Solution2ViewController : UIViewController {
    var current:Int = UISegmentedControlNoSegment

    @IBOutlet weak var mySegmentedControl: UISegmentedControl! {
        didSet {
            guard mySegmentedControl != nil else { return }

            self.mySegmentedControl!.addTarget(
                self,
                action:#selector(changeSelection(sender:)),
                for: .valueChanged)
        }
    }

    func changeSelection(sender: UISegmentedControl) {
        if current == sender.selectedSegmentIndex {
            // user hit the same button again!
            print("user hit the same button again!")
        }
        else {
            current = sender.selectedSegmentIndex
            // user selected a new index
            print("user selected a new index")
        }
    }
}

Solution 3: Si votre approche voulait que le segment sélectionné soit un bouton bascule, la solution 2 pourrait être modifiée pour nettoyer le code comme suit:

class MyToggleSegmentControl : UISegmentedControl {
    /// you could enable or disable the toggle behaviour here
    var shouldToggle:Bool = true

    private var current:Int = UISegmentedControlNoSegment
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        current = self.selectedSegmentIndex
        super.touchesBegan(touches, with: event)
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesEnded(touches, with: event)
        if shouldToggle, current == self.selectedSegmentIndex {
            self.selectedSegmentIndex = UISegmentedControlNoSegment
        }
    }
}

Nous pouvons maintenant nettoyer la fonction changeSelection comme suit:

func changeSelection(sender: UISegmentedControl) {
    switch sender.selectedSegmentIndex {
    case UISegmentedControlNoSegment:
        print("none")
    default:
        print("selected: \(sender.selectedSegmentIndex)")
    }
}

Voici ma solution. Le plus élégant, je pense, si vous voulez que l’événement ValueChanged se déclenche à chaque touche ...

.h

@interface UISegmentedControlAllChanges : UISegmentedControl
@end

.m

@implementation UISegmentedControlAllChanges

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    [self sendActionsForControlEvents:UIControlEventValueChanged];
    [super touchesEnded:touches withEvent:event];
}

@end

La première idée que j'ai eue a été de relier les actions Retoucher à l'intérieur ou Toucher à votre méthode de tri, mais cela ne semble pas fonctionner.

La deuxième idée est plus une solution de contournement, définissez la propriété Momentary sur le contrôle segmenté. Cela déclenchera ensuite une action Value Did Change chaque fois que vous appuyez dessus.

Big help! Ce que je veux faire, c’est que l’option d’un ou de plusieurs boutons soit configurée - et lorsqu'un bouton est défini, un deuxième appui le désactive. Ceci est ma modification:

- (void)setSelectedSegmentIndex:(NSInteger)toValue
{
  // Trigger UIControlEventValueChanged even when re-tapping the selected segment.
  if (toValue==self.selectedSegmentIndex) {
    [super setSelectedSegmentIndex:UISegmentedControlNoSegment]; // notify first
    [self sendActionsForControlEvents:UIControlEventValueChanged]; // then unset
  } else {
    [super setSelectedSegmentIndex:toValue]; 
  }
}

Notify en premier vous permet de lire le contrôle pour trouver le paramètre actuel avant qu'il ne soit réinitialisé.

D'après la réponse de Bob de Graaf:

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

Notez l'utilisation de UIControlEventAllTouchEvents au lieu de UIControlEventValueChanged .

De plus, il n'est pas nécessaire d'appeler - (void) setSelectedSegmentIndex: .

Ci-dessous le morceau de code qui a fonctionné pour moi. Appuyez plusieurs fois sur le même segment pour le désélectionner.

@implementation WUUnselectableSegmentedControl
{
    NSUInteger _selectedIndexBeforeTouches;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    _selectedIndexBeforeTouches = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesEnded:touches withEvent:event];

    if (_selectedIndexBeforeTouches == self.selectedSegmentIndex)
    {
        // Selection didn't change after touch - deselect
        self.selectedSegmentIndex = UISegmentedControlNoSegment;
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

@end

Solution iOS 9. Remplacez UISegmentedControl avec cette classe:

@interface SegmentedControl()

@property (nonatomic) NSInteger previousCurrent;

@end

@implementation SegmentedControl

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    self.previousCurrent = self.selectedSegmentIndex;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [super touchesEnded:touches withEvent:event];

    if (self.selectedSegmentIndex == self.previousCurrent) {
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }

    self.previousCurrent = NSNotFound;
}

@end

Cela semble être une question amusante à répondre. Ce schéma s'étend aux solutions de Steve E et de yershuachu. Cette version utilise un UITapGestureRecognizer pour capturer toutes les touches et définit le selectedSegmentIndex sur -1; mais il transmet également toutes les manipulations à UISegmentedControl afin qu’il puisse gérer toutes les manipulations normales. Aucun sous-classement n'est requis.

UISegmentedControl *mySegControl;

- (void)viewDidLoad
{
    [super viewDidLoad];

    [mySegControl addTarget:self action:@selector(segmentAction:)
           forControlEvents:UIControlEventValueChanged];
    // allow the a seg button tap to be seen even if already selected
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(unitsSegTap:)];
    tapGesture.cancelsTouchesInView = NO; // pass touches through for normal taps
    [mySegControl addGestureRecognizer:tapGesture];

    // continue setup ...
}

- (void)segTap:(UITapGestureRecognizer *)sender
{
    mySegControl.selectedSegmentIndex = -1;
}

// called for UIControlEventValueChanged
- (void)segmentAction:(UISegmentedControl *)sender
{
    NSInteger index = sender.selectedSegmentIndex;
    NSLog(@"unitsSegmentAction %d",(int)index);
    // process the segment
}

J'utilise KVO pour inverser le segment déjà sélectionné pour iOS8.

#import "QCSegmentedControl.h"

static void *QCSegmentedControlContext = &QCSegmentedControlContext;

@implementation QCSegmentedControl

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self registerKVO];
    }

    return self;
}

- (void)dealloc {
    [self removeObserver:self forKeyPath:@"selectedSegmentIndex"];
}

#pragma mark -

- (void)registerKVO {
    [self addObserver:self
           forKeyPath:@"selectedSegmentIndex"
              options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
              context:QCSegmentedControlContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    if (context == QCSegmentedControlContext) {
        NSNumber *new = change[NSKeyValueChangeNewKey];
        NSNumber *old = change[NSKeyValueChangeOldKey];
        if (new.integerValue == old.integerValue) {
            self.selectedSegmentIndex = UISegmentedControlNoSegment;
        }
    }
}

@end

La solution de la réponse sélectionnée ne fonctionnait pas pour moi à partir de la version actuelle iOS v8.x. Mais @Andy propose une solution et je vais compléter:

Sous-classez la UISegmentedControl et remplacez la méthode touchesEnded dans la sous-classe:

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self sendActionsForControlEvents:UIControlEventAllTouchEvents];
    [super touchesEnded:touches withEvent:event];
}

Dans la classe dans laquelle vous souhaitez utiliser UISegmentedControl, créez un iVar currentSelectedSegIndex. Ajoutez les événements UIControlEventAllTouchEvents à côté de vos méthodes d'action UIControlEventValueChanged:

NSInteger currentSelectedSegIndex;

[aSegmentedController addTarget:self action:@selector(aSegmentedControllerSelection:) forControlEvents:UIControlEventValueChanged];
[aSegmentedController addTarget:self action:@selector(aSegmentedControllerAllTouchEvent:) forControlEvents:UIControlEventAllTouchEvents];

Implémentez les deux méthodes d'action:

-(void)aSegmentedControllerAllTouchEvent:(MyUISegmentedControl *)seg
{
    seg.selectedSegmentIndex = UISegmentedControlNoSegment;
}
-(void)aSegmentedControllerSelection:(MyUISegmentedControl *)seg
{
    if (currentSelectedSegIndex == seg.selectedSegmentIndex)
    {
        seg.selectedSegmentIndex = UISegmentedControlNoSegment;
        currentSelectedSegIndex = NSIntegerMax;
        NSLog(@"%s Deselected.", __PRETTY_FUNCTION__);
        // do your stuff if deselected
        return;
    }
    NSLog(@"%s Selected index:%u", __PRETTY_FUNCTION__, seg.selectedSegmentIndex);
    currentSelectedSegIndex = seg.selectedSegmentIndex;
    //do your stuff if selected index
}

Cela devrait fonctionner:

- (IBAction)segmentedControlValueChanged:(id)sender
{
    UISegmentedControl *sc = (UISegmentedControl*) sender;
    switch ([sc selectedSegmentIndex])
    {
       case 0:
            NSLog(@"First option!");
            break;
       case 1:
            NSLog(@"Second option!");
            break;
       case UISegmentedControlNoSegment:
            NSLog(@"No option...");
            break;
        default:
            NSLog(@"No selected option :( ");
    }

    //this resets the segmented control (mySC) value to act as a button.
    [self.mySC setSelectedSegmentIndex:UISegmentedControlNoSegment];
}

N'oubliez pas de connecter ce IBAction à votre sortie de contrôle segmentée Valeur modifiée dans Inspecteur de connexions .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top