iOS7 UISwitch son événement ValueChanged: L'appel continu est ce bug ou quoi ..?

93

Éditer

C'est maintenant fixé sur
Ne faites aucun ajustement pour le réparer.

Modifier2

Apparemment, le même problème se reproduit dans iOS 8.0 et 8.1

Modifier3

C'est maintenant fixé sur
Ne faites aucun ajustement pour le réparer.


Salut Aujourd'hui , j'ai vu dans l' UISwitch'sévénement d' ValueChanged:appel continuously alors que je suis changement Onde Offou Offsur Activé et mon doigt déplacé encore sur le côté droit, ainsi que le côté gauche. J'ai atteché l'image GIF pour plus de clarté avec NSLog.

entrez la description de l'image ici

Ma méthode de changement de valeur est:

- (IBAction)changeSwitch:(id)sender{

    if([sender isOn]){
        NSLog(@"Switch is ON");
    } else{
        NSLog(@"Switch is OFF");
    }
    
}

iOS6 le même code de Switch fonctionne bien comme prévu:

entrez la description de l'image ici

donc quelqu'un peut-il me suggérer cet appel une seule fois son état activé ou désactivé. ou est-ce un bug ou quoi ..?

METTRE À JOUR

Voici ma démo de celui-ci:

Programmatique Ajouter UISwitch

depuis XIB en ajoutant UISwitch

Nitin Gohel
la source
1
Je reçois toujours ce bogue dans iOS7.1 sur le simulateur, je n'ai pas encore essayé d'appareil, exécutant xcode 5.1.1
Fonix
3
Je reçois le même problème avec l'ipad 7.1.2
Hassy
7
Je peux voir un problème identique / similaire avec UISwitch dans iOS 8.0 et 8.1
Sea Coast of Tibet
2
Toujours là en 9.1. Veuillez déposer un double de openradar.appspot.com/15555929 tout le monde. C'est la seule façon de résoudre ce problème.
Guillaume Algis
1
On dirait que c'est de retour en 9.3
Ben Leggiero

Réponses:

44

Veuillez consulter le code suivant:

-(void)viewDidLoad
{
    [super viewDidLoad];    
    UISwitch *mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(130, 235, 0, 0)];    
    [mySwitch addTarget:self action:@selector(changeSwitch:) forControlEvents:UIControlEventValueChanged];
    [self.view addSubview:mySwitch];
}

- (void)changeSwitch:(id)sender{
    if([sender isOn]){
        NSLog(@"Switch is ON");
    } else{
        NSLog(@"Switch is OFF");
    }
}
AeroStar
la source
merci pour la réponse car j'ai dit que j'essayais dans les deux sens et j'ai obtenu le même résultat. atlist je sais comment ajouter swtich programmatique ainsi que de xib monsieur.
Nitin Gohel
12

Même bug ici. Je pense avoir trouvé une solution de contournement simple. Nous devons simplement utiliser un nouveau BOOLqui stocke l'état précédent du UISwitchet une instruction if dans notre IBAction(Value Changed fired) pour vérifier que la valeur du commutateur a réellement changé.

previousValue = FALSE;

[...]

-(IBAction)mySwitchIBAction {
    if(mySwitch.on == previousValue)
        return;
    // resetting the new switch value to the flag
    previousValue = mySwitch.on;
 }

Plus de comportements bizarres. J'espère que ça aide.

Nnarayann
la source
2
cela devrait simplement dire si (mySwitch.on == previousValue)
Keegan Jay
12

Vous pouvez utiliser la propriété UISwitch's .selectedpour vous assurer que votre code ne s'exécute qu'une seule fois lorsque la valeur réelle change. Je pense que c'est une excellente solution car cela évite d'avoir à sous-classer ou à ajouter de nouvelles propriétés.

 //Add action for `ValueChanged`
 [toggleSwitch addTarget:self action:@selector(switchTwisted:) forControlEvents:UIControlEventValueChanged];

 //Handle action
- (void)switchTwisted:(UISwitch *)twistedSwitch
{
    if ([twistedSwitch isOn] && (![twistedSwitch isSelected]))
    {
        [twistedSwitch setSelected:YES];

        //Write code for SwitchON Action
    }
    else if ((![twistedSwitch isOn]) && [twistedSwitch isSelected])
    {
        [twistedSwitch setSelected:NO];

        //Write code for SwitchOFF Action
    }
}

Et le voici à Swift:

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}
itsji10dra
la source
6
Je trouve celui-ci le plus simple.
zekel
+1 car cela m'a conduit à utiliser une solution similaire de remplissage de la valeur .tag lorsque je veux ignorer la logique de bascule. J'ai besoin de la logique pour déclencher parfois en mode marche et arrêt, donc ce qui précède n'était pas tout à fait suffisant.
davidethell
9

Si vous utilisez autant de commutateurs dans votre application, il y a un problème pour changer le code dans tous les endroits où la méthode d'action de UISwitch est définie.Vous pouvez créer un commutateur personnalisé et gérer les événements uniquement si la valeur change.

CustomSwitch.h

#import <UIKit/UIKit.h>

@interface Care4TodayCustomSwitch : UISwitch
@end

CustomSwitch.m

@interface CustomSwitch(){
    BOOL previousValue;
}
@end

@implementation CustomSwitch



- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        previousValue = self.isOn;
    }
    return self;
}


-(void)awakeFromNib{
    [super awakeFromNib];
    previousValue = self.isOn;
    self.exclusiveTouch = YES;
}


- (void)setOn:(BOOL)on animated:(BOOL)animated{

    [super setOn:on animated:animated];
    previousValue = on;
}


-(void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{

    if(previousValue != self.isOn){
        for (id targetForEvent in [self allTargets]) {
            for (id actionForEvent in [self actionsForTarget:targetForEvent forControlEvent:UIControlEventValueChanged]) {
                [super sendAction:NSSelectorFromString(actionForEvent) to:targetForEvent forEvent:event];
            }
        }
        previousValue = self.isOn;
    }
}

@end

Nous ignorons les événements si la valeur est la même que la valeur modifiée.Mettez CustomSwitch dans toute la classe d'UISwitch dans le storyboard.Cela résoudra le problème et n'appellera la cible qu'une seule fois lorsque la valeur change

codester
la source
Cela a fonctionné pour moi. Il est idéal en ce sens qu'il ne provoque pas l'ajout manuel de code superflu dans vos fichiers d'implémentation, car il est réutilisable et cache son implémentation dans l'implémentation de classe. C'est juste un bon design. Ce serait bien si cette réponse avait plus de commentaires, car je ne comprends pas vraiment pourquoi tout le code est là.
James C
Merci, cela a fonctionné pour moi. Pouvez-vous expliquer un peu plus le code. @codester
Swayambhu
7

J'ai de nombreux utilisateurs confrontés au même problème, il se peut donc que ce soit un bogue, UISwitchalors j'ai trouvé une solution temporaire. J'ai trouvé une gitHubcoutumeKLSwitch utilisation pour le moment, j'espère qu'Apple corrigera ce problème dans la prochaine mise à jour de xCode: -

https://github.com/KieranLafferty/KLSwitch

Nitin Gohel
la source
6

Si vous n'avez pas besoin de réagir instantanément au changement de valeur du commutateur, ce qui suit pourrait être une solution:

- (IBAction)switchChanged:(id)sender {
  [NSObject cancelPreviousPerformRequestsWithTarget:self];

  if ([switch isOn]) {
      [self performSelector:@selector(enable) withObject:nil afterDelay:2];
  } else {
      [self performSelector:@selector(disable) withObject:nil afterDelay:2];
  }
}
pré
la source
A fonctionné comme un charme dans iOS 11.2. Dans ma situation, il a déclenché 2 événements d'affilée (état: désactivé, balayage: désactivé, 1er événement: activé, 2ème événement: désactivé), donc un délai de 0,1s me suffit et n'est pas perceptible pour un utilisateur.
Paul Semionov
3

Ce problème est toujours présent à partir de la version bêta d'iOS 9.3. Si cela ne vous dérange pas que l'utilisateur ne puisse pas faire glisser en dehors du commutateur, je trouve que l'utilisation .TouchUpInsideplutôt que .ValueChangedfonctionne de manière fiable.

Nick Kohrn
la source
2

Je suis toujours confronté au même problème sous iOS 9.2

J'ai une solution et je me suis fait passer pour aider les autres

  1. Créer une variable de comptage pour suivre le nombre d'appels de la méthode

    int switchMethodCallCount = 0;
  2. Enregistrer la valeur booléenne pour la valeur de commutation

    bool isSwitchOn = No;
  3. Dans la méthode de changement de valeur de Switch, effectuez l'action souhaitée uniquement pour le premier appel de méthode. Lorsque la valeur de commutation change à nouveau, définissez la valeur de comptage et la valeur de la variable booléenne par défaut

    - (IBAction)frontCameraCaptureSwitchToggle:(id)sender {
    
    
    
    //This method will be called multiple times if user drags on Switch,
    //But desire action should be perform only on first call of this method
    
    
    //1. 'switchMethodCallCount' variable is maintain to check number of calles to method,
    //2. Action is peform for 'switchMethodCallCount = 1' i.e first call
    //3. When switch value change to another state, 'switchMethodCallCount' is reset and desire action perform
    
    switchMethodCallCount++ ;
    
    //NSLog(@"Count --> %d", switchMethodCallCount);
    
    if (switchMethodCallCount == 1) {
    
    //NSLog(@"**************Perform Acction******************");
    
    isSwitchOn = frontCameraCaptureSwitch.on
    
    [self doStuff];
    
    }
    else
    {
    //NSLog(@"Do not perform");
    
    
    if (frontCameraCaptureSwitch.on != isSwitchOn) {
    
        switchMethodCallCount = 0;
    
        isSwitchOn = frontCameraCaptureSwitch.on
    
        //NSLog(@"Count again start");
    
        //call value change method again 
        [self frontCameraCaptureSwitchToggle:frontCameraCaptureSwitch];
    
    
        }
    }
    
    
    }
Chetan Koli
la source
Moi aussi, je suis toujours confronté à ce problème dans 9.2. J'ai implémenté votre logique, et maintenant cela fonctionne comme je l'avais prévu.
Nick Kohrn le
2

Ce problème me tourmente lorsque je lie l'interrupteur à d'autres comportements. Généralement, les choses n'aiment pas aller de onà on. Voici ma solution simple:

@interface MyView : UIView
@parameter (assign) BOOL lastSwitchState;
@parameter (strong) IBOutlet UISwitch *mySwitch;
@end

@implementation MyView

// Standard stuff goes here

- (void)mySetupMethodThatsCalledWhenever
{
    [self.mySwitch addTarget:self action:@selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
}

- (void)switchToggled:(UISwitch *)someSwitch
{
    BOOL newSwitchState = self.mySwitch.on;
    if (newSwitchState == self.lastSwitchState)
    {
        return;
    }
    self.lastSwitchState = newSwitchState;

    // Do your thing
}

Assurez-vous simplement de définir également self.lastSwitchStatechaque fois que vous changez manuellement mySwitch.on! :)

Ben Leggiero
la source
1

Ce type de problème est souvent causé par ValueChanged. Vous n'avez pas besoin d'appuyer sur le bouton pour lancer la fonction. Ce n'est pas un événement tactile. Chaque fois que vous changez par programme le commutateur en marche / arrêt, la valeur change et il appelle à nouveau la fonction IBAction.

@RoNiT a eu la bonne réponse avec:

Rapide

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}
Dave G
la source
0
DispatchQueue.main.async {
        self.mySwitch.setOn(false, animated: true)
    }

Cela fonctionne bien et n'appelle plus la fonction de sélection.

anuraagdjain
la source
0

Cela a fonctionné pour moi.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
user3305074
la source