iOS7 UISwitch的事件值已更改:该Bug或什么..连续调用?


93

编辑

现在固定在
不要做任何调整来修复它。

编辑2

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

编辑3

现在固定在
不要做任何调整来修复它。


今天,嗨,我看到在UISwitch's事件ValueChanged:中调用continuously ,而我的改变OnOff或者Off到在我的手指还在移动在右侧和左侧。我使用NSLog使GIF图像更加清晰。

在此处输入图片说明

我的价值更改方法是:

- (IBAction)changeSwitch:(id)sender{

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

iOS6与Switch相同的代码可以正常工作,正如我们期望的那样:

在此处输入图片说明

因此任何人都可以建议我仅将其状态打开或关闭一次。还是这是错误还是什么?

更新

这是我的演示:

编程添加UISwitch

从XIB添加UISwitch


1
我在模拟器上的iOS7.1中仍然遇到此错误,尚未尝试使用设备,运行xcode 5.1.1
Fonix 2014年

3
我在使用7.1.2 iPad时遇到了同样的问题
Hassy 2014年

7
我可以在iOS 8.0和8.1中看到与UISwitch相同/相似的问题
西藏沿海

2
仍在9.1中。请所有人提交一份openradar.appspot.com/15555929的副本。这是我们要解决此问题的唯一方法。
Guillaume Algis 2015年

1
似乎它回到了9.3
Ben Leggiero

Answers:


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

谢谢回答,因为我说我正在双向尝试并得到相同的结果。atlist我知道如何添加swtich以及xib先生。
Nitin Gohel 2013年

12

同样的错误。我想我找到了一个简单的解决方法。我们只需要使用一个新的BOOL存储了先前状态UISwitch的if和一个if语句(在“ IBAction已触发Value Changed”中)来检查开关的值是否确实发生了变化。

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属性来确保您的代码在实际值更改时仅执行一次。我认为这是一个不错的解决方案,因为它避免了子类化或添加新属性的麻烦。

 //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
我发现这是最简单的。
zekel 2016年

+1的原因是,当我想忽略切换逻辑时,它导致我使用了一种类似的解决方案:填充.tag值。我需要有时在打开和关闭模式下都触发的逻辑,因此上述功能还不够。
davidethell

9

如果您在应用程序中使用了太多的开关,那么在定义UISwitch的t动作方法的所有地方都将无法更改代码。您只能进行自定义开关并仅在值更改时处理事件。

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

如果值与更改后的值相同,我们将忽略事件。将CustomSwitch放在情节提要中的所有UISwitch类中,这将解决问题并仅在更改值后调用target


这对我有用。理想的做法是,它不会引起多余的代码被手动添加到您的实现文件中,因为它可重复使用并将其实现隐藏在类实现中。这只是很好的设计。如果这个答案有更多注释,那就太好了,因为我真的不明白为什么所有代码​​都在那里。
James C

谢谢,对我有用。您能再解释一下代码吗?@codester
Swayambhu


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个事件(状态:关闭,滑动:关闭,第一个事件:打开,第二个事件:关闭),所以0.1s的延迟对我来说足够了,用户不会注意到。
Paul Semionov

3

从iOS 9.3 Beta开始,此问题仍然存在。如果您不介意用户无法将其拖到开关之外,我会发现使用.TouchUpInside而不是.ValueChanged可靠地工作。


2

我仍然在iOS 9.2中面临同样的问题

我得到了解决方案并冒充他人,这可能会帮助其他人

  1. 创建计数变量以跟踪方法被调用的次数

    int switchMethodCallCount = 0;
  2. 将布尔值保存为开关值

    bool isSwitchOn = No;
  3. 在Switch的值更改方法中,仅对第一个方法调用执行期望的操作。当开关值再次更改时,将计数值和t bool变量值设置为默认值

    - (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中面临这个问题。我实现了您的逻辑,现在它已按预期工作。
尼克·科恩

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引起的。您无需按下按钮即可运行该功能。这不是触摸事件。每次以编程方式将开关更改为on / off时,该值都会更改,并再次调用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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.