iOS7 UISwitch its Event ValueChanged: Ciągłe wywoływanie to błąd czy co ..?

93

Edytować

Jest teraz naprawiony
Nie rób żadnych poprawek, aby to naprawić.

Edycja 2

Najwyraźniej ten sam problem występuje ponownie w iOS 8.0 i 8.1

Edycja3

Jest teraz naprawiony
Nie rób żadnych poprawek, aby to naprawić.


Hi Dziś widziałem w UISwitch'sEvent ValueChanged:Wywołanie continuously podczas gdy jestem na zmianę Ondo Offlub Offna On i mój palec przeniósł nadal po prawej stronie, jak również po lewej stronie. Dodałem obraz GIF, aby był bardziej przejrzysty dzięki NSLog.

wprowadź opis obrazu tutaj

Metoda „Zmiana wartości” to:

- (IBAction)changeSwitch:(id)sender{

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

iOS6 ten sam kod Switch działa dobrze, jak się spodziewamy:

wprowadź opis obrazu tutaj

więc może ktoś zasugerować mi, że wywołanie tylko raz jego stan jest włączony lub wyłączony. czy to jest błąd czy co ..?

AKTUALIZACJA

Oto moje demo:

programmatic Dodaj UISwitch

z XIB dodając UISwitch

Nitin Gohel
źródło
1
Nadal otrzymuję ten błąd w iOS7.1 na symulatorze, jeszcze nie wypróbowałem urządzenia, z uruchomionym xcode 5.1.1
Fonix
3
Mam ten sam problem z
iPadem
7
Widzę identyczny / podobny problem z UISwitch w iOS 8.0 i 8.1
Sea Coast of Tibet
2
Nadal tutaj w 9.1. Prosimy o przesłanie duplikatu każdego pliku openradar.appspot.com/15555929 . Tylko w ten sposób możemy to naprawić.
Guillaume Algis
1
Wygląda na to, że wraca w 9.3
Ben Leggiero

Odpowiedzi:

44

Zobacz poniższy kod:

-(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
źródło
dzięki za odpowiedź, jak powiedziałem, próbowałem obu sposobów i uzyskałem ten sam wynik. atlist wiem, jak dodać programmatic swtich, a także od xib sir.
Nitin Gohel
12

Ten sam błąd tutaj. Myślę, że znalazłem proste obejście. Musimy po prostu użyć nowej, BOOLktóra przechowuje poprzedni stan instrukcji UISwitchi if w naszym IBAction(uruchomiono wartość zmieniona), aby sprawdzić, czy wartość przełącznika faktycznie się zmieniła.

previousValue = FALSE;

[...]

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

Nigdy więcej dziwnych zachowań. Mam nadzieję, że to pomoże.

nnarayann
źródło
2
to powinno po prostu powiedzieć if (mySwitch.on == previousValue)
Keegan Jay,
12

Możesz użyć właściwości UISwitch's, .selectedaby upewnić się, że kod zostanie wykonany tylko raz, gdy rzeczywista wartość się zmieni. Myślę, że jest to świetne rozwiązanie, ponieważ pozwala uniknąć podklasy lub dodawania nowych właściwości.

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

A oto w Swift:

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}
itsji10dra
źródło
6
Uważam, że ten jest najprostszy.
zekel
Dał +1, ponieważ doprowadziło mnie to do zastosowania podobnego rozwiązania polegającego na zapełnianiu wartości .tag, gdy chcę zignorować logikę przełączania. Potrzebuję logiki do uruchamiania czasami zarówno w trybie włączonym, jak i wyłączonym, więc powyższe nie było wystarczające.
davidethell
9

Jeśli używasz tak wielu przełączników w swojej aplikacji, to jest problem ze zmianą kodu we wszystkich miejscach, w których jest zdefiniowana metoda działania UISwitch. Możesz ustawić niestandardowy przełącznik i obsłużyć zdarzenia tylko w przypadku zmiany wartości.

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

Ignorujemy zdarzenia, jeśli wartość jest taka sama jak zmieniona wartość. Umieść CustomSwitch we wszystkich klasach UISwitch w scenorysie, co rozwiąże problem i wywoła cel tylko raz, gdy wartość ulegnie zmianie

kodester
źródło
To zadziałało dla mnie. Jest idealny, ponieważ nie powoduje ręcznego dodawania zbędnego kodu do plików implementacji, ponieważ jest wielokrotnego użytku i ukrywa swoją implementację wewnątrz implementacji klasy. To po prostu dobry projekt. Byłoby miło, gdyby ta odpowiedź miała więcej komentarzy, ponieważ tak naprawdę nie rozumiem, dlaczego cały kod jest tam.
James C
Dzięki, u mnie zadziałało. Czy możesz trochę dokładniej wyjaśnić kod. @codester
Swayambhu,
7

Mam wielu użytkowników, którzy mają ten sam problem, więc może to jest błąd, UISwitchwięc właśnie znalazłem tymczasowe rozwiązanie tego problemu. Znalazłem jeden gitHubzwyczajKLSwitch użycie tego na razie, mam nadzieję, że Apple naprawi to w następnej aktualizacji xCode: -

https://github.com/KieranLafferty/KLSwitch

Nitin Gohel
źródło
6

Jeśli nie musisz natychmiast reagować na zmianę wartości przełącznika, rozwiązaniem może być następujące rozwiązanie:

- (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];
  }
}
przed
źródło
Działał jak urok w iOS 11.2. W mojej sytuacji odpalił 2 zdarzenia z rzędu (stan: wyłączone, przeciągnięcie: wyłączone, 1 zdarzenie: włączone, 2 zdarzenie: wyłączone), więc wystarczy mi opóźnienie 0,1 s i nie jest ono zauważalne dla użytkownika.
Paul Semionov
3

Ten problem nadal występuje od wersji beta iOS 9.3. Jeśli nie masz nic przeciwko temu, że użytkownik nie może przeciągać poza przełącznik, uważam, że .TouchUpInsideraczej używa , niż .ValueChangeddziała niezawodnie.

Nick Kohrn
źródło
2

Nadal mam ten sam problem w iOS 9.2

Znalazłem rozwiązanie i udawałem, że może to pomóc innym

  1. Utwórz zmienną count, aby śledzić, ile razy metoda została wywołana

    int switchMethodCallCount = 0;
    
  2. Zapisz wartość bool dla wartości przełącznika

    bool isSwitchOn = No;
    
  3. W metodzie zmiany wartości Switcha wykonaj pożądane działanie tylko dla pierwszego wywołania metody. Po ponownej zmianie wartości ustaw wartość licznika it wartość zmiennej bool na domyślną

    - (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
źródło
Ja również stoję przed tym problemem w 9.2. Wdrożyłem twoją logikę i teraz działa tak, jak zamierzałem.
Nick Kohrn
2

Ten problem nęka mnie, gdy przełączam przełącznik do innych zachowań. Generalnie rzeczy nie lubią przechodzić od ondo on. Oto moje proste rozwiązanie:

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

Pamiętaj tylko, aby ustawić również przy self.lastSwitchStatekażdej ręcznej zmianie mySwitch.on! :)

Ben Leggiero
źródło
1

Ten typ problemu jest często spowodowany zmianą wartości. Nie musisz naciskać przycisku, aby uruchomić funkcję. To nie jest wydarzenie dotykowe. Za każdym razem, gdy programowo zmieniasz przełącznik na on / off, wartość zmienia się i ponownie wywołuje funkcję IBAction.

@RoNiT miał właściwą odpowiedź:

Szybki

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

Działa to dobrze i nie wywołuje ponownie funkcji selektora.

anuraagdjain
źródło
0

To zadziałało dla mnie.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
user3305074
źródło