iOS7 UISwitchのイベントValueChanged:継続的に呼び出すと、このバグまたは何ですか?


93

編集する

修正されました
修正するために微調整を行わないでください。

編集2

どうやら同じ問題がiOS 8.0と8.1で再び発生する

Edit3

修正されました
修正するために微調整を行わないでください。


こんにちは今日はで見られるUISwitch'sイベントValueChanged:の呼び出しcontinuously iは変更していながら、OnOffたりOffオンにし、私の指は左側だけでなく右側にまだ移動しました。私はNSLogでGIF画像をより明確にするためにattechを使用しました。

ここに画像の説明を入力してください

私の値が変更された方法は:

- (IBAction)changeSwitch:(id)sender{

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

iOS6は期待どおりにうまく機能するスイッチの同じコードです。

ここに画像の説明を入力してください

だから、その状態をオンまたはオフにして一度だけ呼び出すことを誰もが提案できますか?または、これはバグか何ですか。

更新

ここにそれが私のデモです:

プログラムによるUISwitchの追加

XIBからUISwitchを追加する


1
私はまだシミュレータ上のiOS7.1でこのバグを取得していますが、デバイスをまだ試していません。xcode5.1.1を実行しています
Fonix

3
7.1.2 ipadで同じ問題が発生します
Hassy、

7
iOS 8.0および8.1のUISwitchで同一/類似の問題が見られる
チベットの海岸

2
まだ9.1にあります。openradar.appspot.com/15555929全員の複製を提出してください。これは、これを修正する唯一の方法です。
Guillaume Algis、2015年

1
9.3に戻ったようです
Ben Leggiero

回答:


44

次のコードをご覧ください。

-(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");
    }
}

私が両方の方法を試していて、同じ結果を得たと私が言ったので、答えはthxです。atlist i knwプログラムでswtichを追加する方法とxib sirから追加する方法。
Nitin Gohel 2013年

12

ここに同じバグ。簡単な回避策を見つけたと思います。スイッチの値が実際に変更されたことを確認するにはBOOL、以前の状態を格納するnew と(Value Changed fired)のUISwitchifステートメントを使用するだけIBActionです。

previousValue = FALSE;

[...]

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

これ以上奇妙な行動はありません。それが役に立てば幸い。


2
if(mySwitch.on == previousValue)
Keegan Jay

12

UISwitch.selectedプロパティを使用して、実際の値が変更されたときにコードが1回だけ実行されるようにすることができます。サブクラス化したり、新しいプロパティを追加したりする必要がないため、これは素晴らしいソリューションだと思います。

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

そして、それはSwiftにあります:

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

6
これが一番簡単だと思います。
ゼケル

これを+1したのは、トグルロジックを無視したいときに.tag値を入力する同様のソリューションを使用するようになったためです。オンモードとオフモードの両方で時々発火するロジックが必要なので、上記では十分ではありませんでした。
davidethell 2017年

9

アプリで非常に多くのスイッチを使用している場合、UISwitchのアクションメソッドが定義されているすべての場所でコードを変更する問題があります。値を変更した場合にのみ、カスタムスイッチを作成してイベントを処理できます。

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

値が変更された値と同じ場合、イベントは無視されます。ストーリーボードのUISwitchのすべてのクラスにCustomSwitchを配置します。これにより、問題が解決され、値が変更されたときにターゲットが1回だけ呼び出されます


これでうまくいきました。再利用可能であり、実装をクラス実装内に隠すため、無関係なコードが実装ファイルに手動で追加されないという点で理想的です。これはちょうど良いデザインです。ただし、この回答にコメントがもっとあれば、すべてのコードが存在する理由がよくわかりません。
James C

おかげで、うまくいきました。コードについてもう少し説明してください。@codester
スワヤンブ

7

私は同じ問題に直面している多くのユーザーを得たので、これはバグかもしれませんUISwitchので、私はそれを一時的に解決するために今見つけました。私は今のところこれを1つのgitHubカスタムKLSwitch使用を見つけました。アップルがxCodeの次のアップデートでこれを修正することを願っています:-

https://github.com/KieranLafferty/KLSwitch


6

スイッチの値の変更に即座に対応する必要がない場合は、次の解決策が考えられます。

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

iOS 11.2の魅力のように働きました。私の状況では、2つのイベントが連続して発生しました(状態:オフ、スワイプ:オフ、最初のイベント:オン、2番目のイベント:オフ)。0.1秒の遅延で十分であり、ユーザーには気づかれません。
Paul Semionov

3

この問題は、iOS 9.3ベータ版でもまだ発生しています。ユーザーがスイッチの外にドラッグできないことを気にしない場合、私は確実に機能するの.TouchUpInsideではなく、使用することを見つけ.ValueChangedます。


2

iOS 9.2でも同じ問題が発生します

私は解決策を得て、他の人を助けるかもしれないのでポーズを取った

  1. メソッドが呼び出された回数を追跡するカウント変数を作成する

    int switchMethodCallCount = 0;
  2. スイッチ値のブール値を保存

    bool isSwitchOn = No;
  3. Switchの値変更メソッドでは、最初のメソッド呼び出しに対してのみ欲求アクションを実行します。スイッチ値が再び設定されたカウント値とブール変数値をデフォルトに変更するとき

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

私も9.2でもこの問題に直面しています。私はあなたのロジックを実装しました、そして今それは私が意図したように機能しています。
Nick Kohrn、2016

2

スイッチを他の動作に結びつけると、この問題は私を悩ませます。一般に、からonに移動することは好ましくありませんon。これが私の簡単な解決策です:

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

self.lastSwitchState手動で変更するたびに必ず設定してくださいmySwitch.on!:)


1

このタイプの問題は、しばしばValueChangedが原因で発生します。関数を実行させるためにボタンを押す必要はありません。タッチイベントではありません。プログラムでスイッチをオン/オフに変更するたびに、値が変更され、IBAction関数が再度呼び出されます。

@RoNiTは正しい答えを持っていました:

迅速

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

これは正常に機能し、セレクター関数を再度呼び出すことはありません。


0

これでうまくいきました。

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()){
    self.switch.isOn = true
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.