iOS7 UISwitch sein Ereignis ValueChanged: Kontinuierliches Aufrufen ist dieser Fehler oder was ..?

93

Bearbeiten

Es ist jetzt behoben
Nehmen Sie keine Änderungen vor, um das Problem zu beheben.

Edit2

Anscheinend tritt das gleiche Problem in iOS 8.0 und 8.1 erneut auf

Edit3

Es ist jetzt behoben
Nehmen Sie keine Änderungen vor, um das Problem zu beheben.


Hallo, heute habe ich in UISwitch'sEvent ValueChanged:Calling gesehen, wie continuously ich Onzu Offoder Offzu Ein gewechselt habe und mein Finger sich immer noch auf der rechten und linken Seite bewegt hat. Ich habe mit NSLog ein GIF-Bild erstellt, um es klarer zu machen.

Geben Sie hier die Bildbeschreibung ein

Meine Methode mit Wertänderung ist:

- (IBAction)changeSwitch:(id)sender{

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

iOS6 funktioniert der gleiche Code von Switch wie erwartet:

Geben Sie hier die Bildbeschreibung ein

kann mir also jemand vorschlagen, der nur einmal seinen zustand ein- oder ausschaltet. oder ist das ein fehler oder was ..?

AKTUALISIEREN

Hier ist meine Demo davon:

programmatisch UISwitch hinzufügen

von XIB Hinzufügen von UISwitch

Nitin Gohel
quelle
1
Ich bekomme immer noch diesen Fehler in iOS7.1 auf Simulator, habe noch kein Gerät ausprobiert und laufe xcode 5.1.1
Fonix
3
Ich bekomme das gleiche Problem mit 7.1.2 ipad
Hassy
7
Ich kann ein identisches / ähnliches Problem mit UISwitch in iOS 8.0 und 8.1
Sea Coast of Tibet
2
Immer noch hier in 9.1. Bitte reichen Sie ein Duplikat von openradar.appspot.com/15555929 ein . Nur so können wir das beheben.
Guillaume Algis
1
Scheint, dass es zurück in 9.3 ist
Ben Leggiero

Antworten:

44

Bitte beachten Sie den folgenden Code:

-(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
quelle
Danke für die Antwort, wie ich sagte, ich habe es in beide Richtungen versucht und das gleiche Ergebnis erzielt. atlist ich wusste, wie man swtich programmatic sowie von xib sir hinzufügt.
Nitin Gohel
12

Gleicher Fehler hier. Ich glaube, ich habe eine einfache Problemumgehung gefunden. Wir müssen nur eine neue verwenden BOOL, die den vorherigen Status der UISwitchund eine if-Anweisung in unserer IBAction(Wert geändert ausgelöst) speichert , um zu überprüfen, ob sich der Wert des Schalters tatsächlich geändert hat.

previousValue = FALSE;

[...]

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

Keine seltsamen Verhaltensweisen mehr. Ich hoffe es hilft.

nnarayann
quelle
2
Dies sollte nur sagen, wenn (mySwitch.on == previousValue)
Keegan Jay
12

Mit der Eigenschaft UISwitch's' können Sie .selectedsicherstellen, dass Ihr Code nur einmal ausgeführt wird, wenn sich der tatsächliche Wert ändert. Ich denke, dies ist eine großartige Lösung, da keine Unterklassen oder neuen Eigenschaften hinzugefügt werden müssen.

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

Und hier ist es in Swift:

func doToggle(switch: UISwitch) {
    if switch.on && !switch.selected {
        switch.selected = true
        // SWITCH ACTUALLY CHANGED -- DO SOMETHING HERE
    } else {
        switch.selected = false
    }
}
itsji10dra
quelle
6
Ich finde das am einfachsten.
Zekel
+ 1 würde dies tun, weil ich eine ähnliche Lösung zum Auffüllen des .tag-Werts verwendet habe, wenn ich die Umschaltlogik ignorieren möchte. Ich brauche die Logik, um manchmal sowohl im Ein- als auch im Aus-Modus zu feuern, daher war das oben Genannte nicht ausreichend.
Davidethell
9

Wenn Sie in Ihrer App so viele Switches verwenden, besteht ein Problem darin, den Code an allen Stellen zu ändern, an denen die Aktionsmethode von UISwitch definiert ist. Sie können benutzerdefinierte Switches vornehmen und die Ereignisse nur behandeln, wenn sich der Wert ändert.

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

Wir ignorieren Ereignisse, wenn der Wert mit dem geänderten Wert übereinstimmt. Fügen Sie CustomSwitch in die gesamte Klasse von UISwitch im Storyboard ein. Dadurch wird das Problem behoben und das Ziel nur einmal aufgerufen, wenn sich der Wert ändert

Codester
quelle
Das hat bei mir funktioniert. Dies ist insofern ideal, als fremder Code nicht manuell in Ihre Implementierungsdateien eingefügt wird, da er wiederverwendbar ist und seine Implementierung innerhalb der Klassenimplementierung verbirgt. Das ist einfach gutes Design. Es wäre schön, wenn diese Antwort mehr Kommentare hätte, da ich nicht wirklich verstehe, warum der gesamte Code vorhanden ist.
James C
Danke, es hat bei mir funktioniert. Können Sie den Code etwas näher erläutern? @codester
Swayambhu
7

Ich habe viele Benutzer, die mit dem gleichen Problem konfrontiert sind. Vielleicht ist dies ein Fehler, UISwitchalso habe ich gerade eine vorübergehende Lösung gefunden. Ich habe einen gitHubBenutzer gefunden KLSwitch, der dies für den Moment verwendet. Ich hoffe, Apple wird dies im nächsten Update von xCode beheben: -

https://github.com/KieranLafferty/KLSwitch

Nitin Gohel
quelle
6

Wenn Sie nicht sofort auf die Wertänderung des Schalters reagieren müssen, kann Folgendes eine Lösung sein:

- (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];
  }
}
Vor
quelle
Funktionierte wie ein Zauber in iOS 11.2. In meiner Situation wurden 2 Ereignisse hintereinander ausgelöst (Status: Aus, Wischen: Aus, 1. Ereignis: Ein, 2. Ereignis: Aus), sodass eine Verzögerung von 0,1 s für mich ausreicht und für einen Benutzer nicht erkennbar ist.
Paul Semionov
3

Dieses Problem ist ab iOS 9.3 Beta immer noch vorhanden. Wenn es Ihnen nichts ausmacht, dass der Benutzer nicht außerhalb des Schalters ziehen kann, funktioniert das .TouchUpInsideeher, als dass es .ValueChangedzuverlässig funktioniert.

Nick Kohrn
quelle
2

Ich habe immer noch das gleiche Problem in iOS 9.2

Ich habe Lösung und posiere, weil es anderen helfen könnte

  1. Erstellen Sie eine Zählvariable, um zu verfolgen, wie oft die Methode aufgerufen wurde

    int switchMethodCallCount = 0;
  2. Speichern Sie den Bool-Wert für den Schalterwert

    bool isSwitchOn = No;
  3. Führen Sie in der Wertänderungsmethode von Switch die Wunschaktion nur für den ersten Methodenaufruf aus. Wenn der Schaltwert erneut geändert wird, setzen Sie den Zählwert und den Wert der Bool-Variablen auf den Standardwert

    - (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
quelle
Auch ich bin in 9.2 immer noch mit diesem Problem konfrontiert. Ich habe Ihre Logik implementiert und jetzt funktioniert sie wie beabsichtigt.
Nick Kohrn
2

Dieses Problem plagt mich, wenn ich den Schalter mit anderen Verhaltensweisen verbinde. Im Allgemeinen gehen die Dinge nicht gerne von onnach on. Hier ist meine einfache Lösung:

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

Stellen Sie nur sicher, dass Sie dies auch self.lastSwitchStatejedes Mal einstellen, wenn Sie manuell ändern mySwitch.on! :) :)

Ben Leggiero
quelle
1

Diese Art von Problem wird häufig durch ValueChanged verursacht. Sie müssen die Taste nicht drücken, damit die Funktion ausgeführt wird. Es ist kein Touch-Event. Jedes Mal, wenn Sie den Schalter programmgesteuert auf Ein / Aus stellen, ändert sich der Wert und die IBAction-Funktion wird erneut aufgerufen.

@RoNiT hatte die richtige Antwort mit:

Schnell

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

Dies funktioniert einwandfrei und ruft die Auswahlfunktion nicht erneut auf.

anuraagdjain
quelle
0

Das hat bei mir funktioniert.

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