iOS7 UISwitch its Event ValueChanged: Chamar continuamente é este Bug ou o quê ..?


93

Editar

Agora está fixado em
Não faça nenhum ajuste para consertar.

Edit2

Aparentemente, o mesmo problema acontece novamente no iOS 8.0 e 8.1

Edit3

Agora está fixado em
Não faça nenhum ajuste para consertar.


Oi Hoje eu visto em UISwitch'seventos ValueChanged:de chamada continuously , enquanto eu sou a mudança para Ona Offou Offpara On e meu dedo se moveu ainda no lado direito, bem como do lado esquerdo. Eu anexei a imagem GIF para mais clara com NSLog.

insira a descrição da imagem aqui

Meu método de alteração de valor é:

- (IBAction)changeSwitch:(id)sender{

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

iOS6 o mesmo código de Switch funcionando bem como esperamos:

insira a descrição da imagem aqui

então, alguém pode me sugerir que a chamada apenas uma vez esteja ligada ou desligada. ou isso é um bug ou o quê ..?

ATUALIZAR

Aqui está a minha demonstração disso:

Adicionar UISwitch programático

de XIB adicionando UISwitch


1
Ainda estou recebendo este bug no iOS7.1 no simulador, não tentei o dispositivo ainda, executando o xcode 5.1.1
Fonix

3
Estou tendo o mesmo problema com ipad 7.1.2
Hassy

7
Eu posso ver um problema idêntico / semelhante com UISwitch no iOS 8.0 e 8.1
Sea Coast of Tibet,

2
Ainda aqui em 9.1. Envie uma cópia de openradar.appspot.com/15555929 para todos. Essa é a única maneira de consertar isso.
Guillaume Algis

1
Parece que está de volta em 9.3
Ben Leggiero

Respostas:


44

Por favor, veja o seguinte 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");
    }
}

Obrigado pela resposta como eu disse que estava tentando dos dois lados e obtive o mesmo resultado. atlist eu sei como adicionar programático swtich, bem como do xib senhor.
Nitin Gohel

12

Mesmo bug aqui. Acho que encontrei uma solução alternativa simples. Só precisamos usar um novo BOOLque armazene o estado anterior do UISwitche uma instrução if em nosso IBAction(Valor alterado disparado) para verificar se o valor do switch realmente mudou.

previousValue = FALSE;

[...]

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

Não há mais comportamentos estranhos. Espero que ajude.


2
deve apenas dizer se (mySwitch.on == previousValue)
Keegan Jay

12

Você pode usar o UISwitch's .selectedpropriedade para certificar-se de seu código executa somente uma vez quando o valor de mudanças reais. Acho que essa é uma ótima solução porque evita ter que criar subclasses ou adicionar novas propriedades.

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

E aqui está em Swift:

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

6
Acho que este é o mais simples.
zekel de

Marquei com +1 porque me levou a usar uma solução semelhante de preencher o valor .tag quando desejo ignorar a lógica de alternância. Eu preciso que a lógica seja acionada às vezes tanto no modo ligado quanto desligado, então o acima não foi suficiente.
davidethell

9

Se você estiver usando tantos interruptores em seu aplicativo, então há um problema para alterar o código em todos os lugares onde o método de ação do UISwitch é definido. Você pode fazer alternância personalizada e lidar com os eventos somente se o valor mudar.

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 eventos se o valor for igual ao valor alterado. Coloque CustomSwitch em toda a classe de UISwitch no storyboard. Isso resolverá o problema e chamará o destino apenas uma vez quando o valor for alterado


Isso funcionou para mim. É ideal porque não faz com que código estranho seja adicionado manualmente em seus arquivos de implementação, pois é reutilizável e oculta sua implementação dentro da implementação da classe. Este é apenas um bom design. Seria bom se esta resposta tivesse mais comentários, porque eu realmente não entendo por que todo o código está lá.
James C

Obrigado, funcionou para mim. Você pode explicar o código um pouco mais. @codester
Swayambhu

7

Eu tenho muitos usuários que enfrentam o mesmo problema, então pode ser que seja um bug de UISwitchentão eu encontrei agora para solução temporária do mesmo. Eu encontrei um uso gitHubpersonalizado KLSwitchpara agora, espero que a apple conserte isso na próxima atualização do xCode: -

https://github.com/KieranLafferty/KLSwitch


6

Se você não precisa reagir instantaneamente à mudança de valor do switch, o seguinte pode ser uma solução:

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

Funcionou perfeitamente no iOS 11.2. Na minha situação, ele disparou 2 eventos consecutivos (estado: desligado, deslizando: desligado, 1º evento: ligado, 2º evento: desligado), então um atraso de 0,1s é suficiente para mim e não perceptível para um usuário.
Paul Semionov

3

Este problema ainda existia no iOS 9.3 beta. Se você não se importa que o usuário não consiga arrastar para fora do switch, acho que usar, em .TouchUpInsidevez de .ValueChangedfuncionar, é confiável.


2

Ainda estou enfrentando o mesmo problema no iOS 9.2

Eu tenho a solução e poso como se pudesse ajudar outras pessoas

  1. Crie uma variável de contagem para rastrear o número de vezes que o método foi chamado

    int switchMethodCallCount = 0;
  2. Salve o valor bool para o valor da chave

    bool isSwitchOn = No;
  3. No método de mudança de valor de Switch, execute a ação desejada apenas para a primeira chamada de método. Quando o valor de troca muda novamente o valor do conjunto de contagem e o valor da variável bool para o padrão

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

Eu também ainda estou enfrentando esse problema em 9.2. Implementei sua lógica e agora está funcionando como planejado.
Nick Kohrn

2

Esse problema me incomoda quando vinculo a chave a outros comportamentos. Geralmente as coisas não gostam de ir de onpara on. Esta é minha solução simples:

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

Apenas certifique-se de definir self.lastSwitchStatesempre que você mudar manualmente mySwitch.on! :)


1

Esse tipo de problema geralmente é causado por ValueChanged. Você não precisa pressionar o botão para fazer com que a função seja executada. Não é um evento de toque. Cada vez que você altera programaticamente a chave para on / off, o valor muda e ele chama a função IBAction novamente.

@RoNiT teve a resposta certa com:

Rápido

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

Isso funciona bem e não chama a função do seletor novamente.


0

Isso funcionou para mim.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.