iOS7 UISwitch your Event ValueChanged: Llamar continuamente es este error o qué ...?

93

Editar

Ahora está arreglado
No hagas ningún ajuste para solucionarlo.

Editar2

Al parecer, vuelve a ocurrir el mismo problema en iOS 8.0 y 8.1

Editar3

Ahora está arreglado
No hagas ningún ajuste para solucionarlo.


Hola Hoy he visto en UISwitch'seventos ValueChanged:de llamada continuously mientras estoy a cambio Onde Offo Offen On y mi dedo se movió todavía en el lado derecho, así como el lado izquierdo. Asistí a la imagen GIF para obtener más claridad con NSLog.

ingrese la descripción de la imagen aquí

Mi método de cambio de valor es:

- (IBAction)changeSwitch:(id)sender{

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

iOS6 el mismo código de Switch funcionando bien como esperábamos:

ingrese la descripción de la imagen aquí

Entonces, ¿alguien puede sugerirme que llame solo una vez a su estado Activado o desactivado? o esto es un error o qué ..?

ACTUALIZAR

Aquí está mi demostración:

programática Agregar UISwitch

desde XIB agregando UISwitch

Nitin Gohel
fuente
1
Todavía tengo este error en iOS7.1 en el simulador, aún no he probado el dispositivo, ejecutando xcode 5.1.1
Fonix
3
Tengo el mismo problema con el ipad 7.1.2
Hassy
7
Puedo ver un problema idéntico / similar con UISwitch en iOS 8.0 y 8.1
Sea Coast of Tibet
2
Todavía aquí en 9.1. Por favor, presente un duplicado de openradar.appspot.com/15555929 para todos. Esta es la única forma en que vamos a arreglar esto.
Guillaume Algis
1
Parece que está de vuelta en 9.3
Ben Leggiero

Respuestas:

44

Consulte el siguiente código:

-(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
fuente
gracias por la respuesta, como dije, estaba intentando en ambos sentidos y obtuve el mismo resultado. atlist sé cómo agregar swtich programática, así como de xib señor.
Nitin Gohel
12

El mismo error aquí. Creo que he encontrado una solución sencilla. Solo tenemos que usar un nuevo BOOLque almacene el estado anterior del UISwitchy una instrucción if en nuestro IBAction(Valor cambiado activado) para verificar que el valor del conmutador realmente haya cambiado.

previousValue = FALSE;

[...]

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

No más comportamientos extraños. Espero eso ayude.

nnarayann
fuente
2
esto solo debería decir si (mySwitch.on == previousValue)
Keegan Jay
12

Se puede utilizar el UISwitch's .selectedpropiedad para asegurarse de que su código sólo se ejecuta una vez cuando cambia el valor real. Creo que esta es una gran solución porque evita tener que crear una subclase o agregar nuevas propiedades.

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

Y aquí está en Swift:

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}
itsji10dra
fuente
6
Encuentro que este es el más simple.
zekel
Hice +1 en esto porque me llevó a usar una solución similar para completar el valor .tag cuando quiero ignorar la lógica de alternancia. Necesito que la lógica se active a veces tanto en modo encendido como apagado, por lo que lo anterior no fue suficiente.
davidethell
9

Si está utilizando tantos interruptores en su aplicación, entonces hay un problema para cambiar el código en todos los lugares donde se define el método de acción t de UISwitch. Puede hacer un cambio personalizado y manejar los eventos solo si el valor 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

Estamos ignorando los eventos si el valor es el mismo que el valor cambiado. Ponga CustomSwitch en toda la clase de UISwitch en el guión gráfico. Esto resolverá el problema y llamará al objetivo solo una vez cuando el valor cambie

codificador
fuente
Esto funcionó para mí. Es ideal porque no hace que se agregue manualmente código extraño en sus archivos de implementación, ya que es reutilizable y oculta su implementación dentro de la implementación de la clase. Este es un buen diseño. Sin embargo, sería bueno si esta respuesta tuviera más comentarios, porque realmente no entiendo por qué todo el código está allí.
James C
Gracias, funcionó para mí. ¿Puedes explicar el código un poco más? @codester
Swayambhu
7

Tengo muchos usuarios que enfrentan el mismo problema, por lo que puede ser un error, UISwitchasí que encontré una solución temporal. Encontré uno gitHubpersonalizadoKLSwitch uso esto por ahora, espero que Apple solucione esto en la próxima actualización de xCode: -

https://github.com/KieranLafferty/KLSwitch

Nitin Gohel
fuente
6

Si no necesita reaccionar instantáneamente al cambio de valor del interruptor, lo siguiente podría ser una solución:

- (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];
  }
}
pre
fuente
Funcionó como un encanto en iOS 11.2. En mi situación, disparó 2 eventos seguidos (estado: apagado, deslizamiento: apagado, 1er evento: encendido, 2do evento: apagado), por lo que un retraso de 0.1s es suficiente para mí y no lo nota un usuario.
Paul Semionov
3

Este problema todavía está aquí a partir de la versión beta de iOS 9.3. Si no le importa que el usuario no pueda arrastrar fuera del interruptor, creo que usar en .TouchUpInsidelugar de .ValueChangedfunciona de manera confiable.

Nick Kohrn
fuente
2

Todavía tengo el mismo problema en iOS 9.2

Tengo una solución y me hago pasar por eso que podría ayudar a otros

  1. Crear una variable de recuento para rastrear el número de veces que el método recibió una llamada

    int switchMethodCallCount = 0;
  2. Guardar el valor bool para el valor de cambio

    bool isSwitchOn = No;
  3. En el método de cambio de valor de Switch, realice la acción deseada solo para la primera llamada al método. Cuando el valor del interruptor cambia nuevamente, establece el valor de conteo y el valor de la variable bool a los predeterminados

    - (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
fuente
Yo también sigo enfrentando este problema en 9.2. Implementé su lógica y ahora está funcionando como tenía previsto.
Nick Kohrn
2

Este problema me atormenta cuando ato el cambio a otros comportamientos. Generalmente, a las cosas no les gusta ir del ona on. Aquí está mi solución 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
}

¡Solo asegúrese de configurar también self.lastSwitchStatecada vez que cambie manualmente mySwitch.on! :)

Ben Leggiero
fuente
1

Este tipo de problema a menudo es causado por ValueChanged. No es necesario presionar el botón para que se ejecute la función. No es un evento táctil. Cada vez que cambia programáticamente el interruptor a encendido / apagado, el valor cambia y vuelve a llamar a la función IBAction.

@RoNiT tuvo la respuesta correcta con:

Rápido

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

Esto funciona bien y no vuelve a llamar a la función de selector.

anuraagdjain
fuente
0

Esto funcionó para mí.

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