IOS7 UISswitch 其事件值改变: 连续调用是这个错误还是什么... ?

剪辑

现在它被固定在了 ios7.1的类 = “ post-tag”title = “ show questions tag & # 39; ios7.1 & # 39;”rel = “ tag”> ios7.1上
不要做任何调整来修复它。

编辑2

显然,同样的问题在 iOS 8.0和8.1中再次发生

编辑3

现在它被固定在了 ios9.2的类 = “ post-tag”title = “ show questions tags & # 39; ios9.2 & # 39;”rel = “ tag”> ios9.2上
不要做任何调整来修复它。


嗨,今天我看到在 UISwitch's事件 ValueChanged:呼叫 continuously当我改变到 OnOff或者 Off到 On 和我的手指移动仍然在右边以及左边。为了使 NSLog 更清晰,我添加了 GIF 图像。

enter image description here

我的价值改变方法是:

- (IBAction)changeSwitch:(id)sender{


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

}

IOS6的 Switch 代码与我们预期的相同:

enter image description here

so can anyone suggest me that call only one time its state On or off. or is this is a bug or what..?

更新

这是我的演示:

程序化添加 UISswitch

从 XIB 加入 UISswitch

72021 次浏览

I got many user that facing same issue so may be this is bug of UISwitch so i found just now for temporary solution of it. I Found one gitHub custom KLSwitch use this for now hope apple will fix this in next update of xCode:-

https://github.com/KieranLafferty/KLSwitch

Please see the following 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");
}
}

If you don't need to react instantly to the switch's value change the following could be a solution:

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

Same bug here. I think I've found a simple workaround. We just have to use a new BOOL that stores the previous state of the UISwitch and an if statement in our IBAction (Value Changed fired) to check that the value of the switch has actually changed.

previousValue = FALSE;


[...]


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

No more weird behaviors. Hope it helps.

If you are using so many switch in your app then there is problem to change the code in all places where t action method of UISwitch is defined.You can make custom switch and handle the events only if value change.

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

We are ignoring events if the value is same as changed value.Put CustomSwitch in all the class of UISwitch in storyboard.This will resolve the issue and call target only once when value changed

You can use the UISwitch's .selected property to make sure your code only executes once when the value actual changes. I think this is a great solution because it avoids having to subclass or add new properties.

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

And here it is in Swift:

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

I am still facing same problem in iOS 9.2

I got solution and posing as it might help others

  1. Create count variable to trace number of times method got call

    int switchMethodCallCount = 0;
    
  2. Save bool value for switch value

    bool isSwitchOn = No;
    
  3. In Switch's value change method perform desire action for first method call only. When switch value again changes set count value andt bool variable value to default

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

This issue is still here as of iOS 9.3 beta. If you don't mind the user not being able to drag outside of the switch, I find that using .TouchUpInside rather than .ValueChanged works reliably.

This problem plagues me when I tie the switch to other behaviors. Generally things don't like to go from on to on. Here's my simple solution:

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

Just be sure to also set self.lastSwitchState every time you manually change mySwitch.on! :)

This type of problem is often caused by ValueChanged. You don't need to press the button to cause the function to run. It's not a touch event. Every time you programmatically change the switch to on/off, the value changes and it calls the IBAction function again.

@RoNiT had the right answer with:

Swift

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

This works fine and doesn't calls the selector function again.

This worked for me.

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