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.