web-dev-qa-db-fra.com

iOS7 UISwitch son événement ValueChanged: L'appel est en permanence ce bogue ou quoi ..?

Modifier

Il est maintenant fixé sur ios7.1
Ne faites pas de tweak pour le réparer.

Edit2

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

Edit3

Il est maintenant fixé sur ios9.2
Ne faites pas de tweak pour le réparer.


Bonjour, j’ai vu dans UISwitch's Evénement ValueChanged: Appeler continuously alors que je suis passé à On à Off ou Off à On et mon doigt s’est déplacé toujours du côté droit au côté gauche. J'ai créé une image GIF pour plus de clarté avec NSLog.

enter image description here

La méthode Ma valeur modifiée est:

- (IBAction)changeSwitch:(id)sender{

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

}

iOS6 le même code de commutateur fonctionne bien comme nous l'attendions:

enter image description here

alors quelqu'un peut-il me suggérer d'appeler une seule fois son état On ou Off. ou est-ce un bug ou quoi ..?

METTRE À JOUR

Voici ma démo:

programmatique Add UISwitch

de XIB ajoutant UISwitch

91
Nitin Gohel

J'ai eu beaucoup d'utilisateurs qui font face au même problème alors peut-être que c'est un bogue de UISwitch alors j'ai trouvé tout à l'heure une solution temporaire. J'ai trouvé un gitHub personnalisé KLSwitch utiliser ceci pour le moment, j'espère que Apple corrigera cela dans la prochaine mise à jour de xCode: -

https://github.com/KieranLafferty/KLSwitch

7
Nitin Gohel

S'il vous plaît voir 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");
    }
}
44
AeroStar

Même bug ici. Je pense avoir trouvé une solution de contournement simple. Nous devons simplement utiliser une nouvelle BOOL qui stocke l’état précédent de la UISwitch et une instruction if dans notre IBAction (valeur modifiée), 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;
 }

Pas plus de comportements étranges. J'espère que ça aide.

12
nnarayann

Vous pouvez utiliser la propriété .selected de la UISwitch 'pour vous assurer que votre code ne s'exécute qu'une fois lorsque la valeur réelle est modifiée. Je pense que c'est une excellente solution car elle é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
    }
}
12
itsji10dra

Si vous utilisez autant de commutateurs dans votre application, vous rencontrez des difficultés pour modifier le code dans tous les emplacements où la méthode d'action t 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 identique à la valeur modifiée. Placez 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 sera modifiée. 

9
codester

Si vous n'avez pas besoin de réagir instantanément au changement de valeur du commutateur, voici 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];
  }
}
6
pre

Ce problème est toujours là depuis iOS 9.3 beta. Si cela ne vous dérange pas que l'utilisateur ne puisse pas glisser en dehors du commutateur, l'utilisation de .TouchUpInside plutôt que .ValueChanged fonctionne de manière fiable.

3
Nick Kohrn

Ce problème me tourmente lorsque je lie le passage à d’autres comportements. Généralement, les choses ne vont pas 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
}

Veillez simplement à définir également self.lastSwitchState chaque fois que vous modifiez manuellement mySwitch.on! :)

2
Ben Leggiero

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

J'ai eu la solution et posant comme cela pourrait aider les autres 

  1. Créer une variable de comptage pour tracer 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 pour le premier appel de méthode uniquement. Lorsque la valeur du commutateur change à nouveau, définissez la valeur de comptage et la valeur de variable bool sur la valeur 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];
    
    
        }
    }
    
    
    }
    
2
Chetan Koli

Ce type de problème est souvent causé par ValueChanged. Vous n'avez pas besoin d'appuyer sur le bouton pour que la fonction s'exécute. Ce n'est pas un événement tactile. Chaque fois que vous réglez le commutateur sur ON/OFF par programmation, la valeur change et il appelle à nouveau la fonction IBAction. 

@RoNiT avait 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
    }
}
1
Dave G

Cela a fonctionné pour moi.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
0
user3305074
DispatchQueue.main.async {
        self.mySwitch.setOn(false, animated: true)
    }

Cela fonctionne bien et n'appelle pas à nouveau la fonction de sélection.

0
anuraagdjain