iOS7 UISwitch its Event ValueChanged: Calling continuamente is this Bug or what ..?


93

modificare

Ora è risolto
Non apportare modifiche per risolverlo.

Modifica 2

Apparentemente lo stesso problema si verifica di nuovo in iOS 8.0 e 8.1

Modifica 3

Ora è risolto
Non apportare modifiche per risolverlo.


Ciao oggi ho visto in UISwitch'sEvent ValueChanged:Calling continuously mentre sono passato Ona Offo Offa On e il mio dito si è spostato ancora sul lato destro e sul lato sinistro. Ho attaccato un'immagine GIF per una maggiore chiarezza con NSLog.

inserisci qui la descrizione dell'immagine

Il mio metodo Value Changed è:

- (IBAction)changeSwitch:(id)sender{

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

iOS6 lo stesso codice di Switch funziona bene come ci aspettiamo:

inserisci qui la descrizione dell'immagine

quindi qualcuno può suggerirmi di chiamare solo una volta il suo stato On o off. o è un bug o cosa ..?

AGGIORNARE

Eccone la mia Demo:

a livello di codice Aggiungi UISwitch

da XIB aggiungendo UISwitch


1
Sto ancora riscontrando questo bug in iOS7.1 sul simulatore, non ho ancora provato il dispositivo, eseguendo xcode 5.1.1
Fonix


7
Riesco a vedere un problema identico / simile con UISwitch in iOS 8.0 e 8.1
Sea Coast of Tibet

2
Ancora qui in 9.1. Si prega di presentare un duplicato di openradar.appspot.com/15555929 a tutti. Questo è l'unico modo per risolverlo.
Guillaume Algis

1
Sembra che sia tornato in 9.3
Ben Leggiero

Risposte:


44

Si prega di consultare il codice seguente:

-(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");
    }
}

grazie per la risposta come ho detto che stavo provando in entrambi i modi e ho ottenuto lo stesso risultato. atlist so come aggiungere swtich programmatic così come da xib sir.
Nitin Gohel

12

Stesso bug qui. Penso di aver trovato una soluzione semplice. Dobbiamo solo usare un nuovo BOOLche memorizza lo stato precedente del UISwitche un'istruzione if nel nostro IBAction(Value Changed fired) per verificare che il valore dello switch sia effettivamente cambiato.

previousValue = FALSE;

[...]

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

Niente più comportamenti strani. Spero che sia d'aiuto.


2
questo dovrebbe solo dire if (mySwitch.on == previousValue)
Keegan Jay

12

Puoi usare il file UISwitch.selected proprietà 's per assicurarti che il tuo codice venga eseguito solo una volta quando il valore effettivo cambia. Penso che questa sia un'ottima soluzione perché evita di dover sottoclassare o aggiungere nuove proprietà.

 //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
    }
}

Ed eccolo qui in Swift:

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}

6
Trovo che questo sia il più semplice.
zekel

Ho fatto +1 perché mi ha portato a utilizzare una soluzione simile per popolare il valore .tag quando voglio ignorare la logica di commutazione. Ho bisogno che la logica si attivi a volte sia in modalità on che off, quindi quanto sopra non era abbastanza sufficiente.
davidethell

9

Se stai usando così tanti switch nella tua app, c'è un problema nel cambiare il codice in tutti i posti in cui è definito il metodo di azione di UISwitch. Puoi fare un interruttore personalizzato e gestire gli eventi solo se il valore cambia.

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

Stiamo ignorando gli eventi se il valore è uguale al valore modificato.Metti CustomSwitch in tutta la classe di UISwitch nello storyboard.Questo risolverà il problema e chiamerà il target solo una volta quando il valore è cambiato


Questo ha funzionato per me. È ideale in quanto non causa l'aggiunta manuale di codice estraneo ai file di implementazione, poiché è riutilizzabile e nasconde la sua implementazione all'interno dell'implementazione della classe. Questo è solo un buon design. Sarebbe bello se questa risposta avesse più commenti, perché non capisco davvero perché tutto il codice sia lì.
James C

Grazie, ha funzionato per me. Puoi spiegare un po 'di più il codice. @codester
Swayambhu

7

Ho molti utenti che affrontano lo stesso problema, quindi potrebbe essere un bug, UISwitchquindi ho trovato solo ora una soluzione temporanea. Ho trovato gitHubun'usanzaKLSwitch uso per ora spero che Apple risolva il problema nel prossimo aggiornamento di xCode: -

https://github.com/KieranLafferty/KLSwitch


6

Se non è necessario reagire immediatamente alla modifica del valore dell'interruttore, la seguente potrebbe essere una soluzione:

- (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];
  }
}

Ha funzionato a meraviglia in iOS 11.2. Nella mia situazione, ha attivato 2 eventi di seguito (stato: spento, scorrimento: spento, 1 ° evento: acceso, 2 ° evento: spento), quindi un ritardo di 0,1 s è sufficiente per me e non visibile a un utente.
Paul Semionov

3

Questo problema è ancora presente a partire da iOS 9.3 beta. Se non ti dispiace che l'utente non sia in grado di trascinare al di fuori dell'interruttore, trovo che l'uso .TouchUpInsidepiuttosto che .ValueChangedfunzioni in modo affidabile.


2

Sto ancora affrontando lo stesso problema in iOS 9.2

Ho trovato una soluzione e fingendo di poter aiutare gli altri

  1. Crea una variabile di conteggio per tenere traccia del numero di volte in cui il metodo ha ricevuto la chiamata

    int switchMethodCallCount = 0;
  2. Salva il valore bool per il valore dell'interruttore

    bool isSwitchOn = No;
  3. Nel metodo di modifica del valore di Switch, eseguire l'azione desiderata solo per la prima chiamata al metodo. Quando il valore dell'interruttore cambia di nuovo, impostare il valore di conteggio e il valore della variabile bool al valore predefinito

    - (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];
    
    
        }
    }
    
    
    }

Anch'io sto ancora affrontando questo problema nella 9.2. Ho implementato la tua logica e ora funziona come avevo previsto.
Nick Kohrn

2

Questo problema mi affligge quando lego il passaggio ad altri comportamenti. In genere alle cose non piace andare da ona on. Ecco la mia semplice soluzione:

@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
}

Assicurati solo di impostare anche self.lastSwitchStateogni volta che cambi manualmente mySwitch.on! :)


1

Questo tipo di problema è spesso causato da ValueChanged. Non è necessario premere il pulsante per avviare la funzione. Non è un evento tattile. Ogni volta che si modifica a livello di codice l'interruttore su on / off, il valore cambia e viene chiamata nuovamente la funzione IBAction.

@RoNiT ha dato la risposta giusta con:

Swift

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}

0
DispatchQueue.main.async {
        self.mySwitch.setOn(false, animated: true)
    }

Funziona bene e non chiama più la funzione selector.


0

Questo ha funzionato per me.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.