John
John

Reputation: 307

Detecting a change in UISwitch

This sounds trivial but I'm noticing some weirdness. I've wired up a handler for the Value Changed event of a UISwitch. What I would expect is that each time the handler is called the value of the switch would change. But that's actually not always the case. If you press the switch rapidly on/off the handler can get called consecutively with the SAME state for the switch (in my specific application this is an issue). So I'm wondering if anyone else has noticed this behavior and has figured out a good solution.

Upvotes: 27

Views: 35376

Answers (8)

Vadim Bulavin
Vadim Bulavin

Reputation: 3867

In iOS 11 a new UISwitch bug was introduced so I don't recommend subscribing to value changed events. Otherwise your callback will be triggered every time UISwitch's isOn attribute changes programmatically.

Instead:

  1. Subscribe for touch up inside event:
let switch = UISwitch()
switch.addTarget(self, action: #selector(onSwitchValueChanged), for: .valueChanged)
  1. Implement the callback method:
func onSwitchValueChanged(_ switch: UISwitch) {
        
}
  1. And now when you programmatically change isOn value, it won't trigger onSwitchValueChanged method.

switch.isOn = !switch.isOn // 'onSwitchValueChanged' is not triggered

Upvotes: 11

Mike
Mike

Reputation: 9835

My problem was a stupid one... I was expecting the enabled value to change, but obviously that isn't the correct value to inspect upon the toggle of the switch, the on or isOn is the correct thing to use.

Upvotes: 0

user1105951
user1105951

Reputation: 2287

   -(void) createSwitch
    {
        self.searchExistSearchNewSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0,0,0,0)];
        [self.searchExistSearchNewSwitch addTarget:self action:@selector(switchValueChanged:) forControlEvents:UIControlEventValueChanged];
        [self.view addSubview:self.searchExistSearchNewSwitch];
    }
    - (void)switchValueChanged:(UISwitch *)theSwitch
    {
        BOOL flag = theSwitch.isOn;
    }

Upvotes: 19

Leslie Godwin
Leslie Godwin

Reputation: 2661

Here's a solution that works for me. It also sends a property "will/did change" notification when the switch is changed. The event also functions correctly in that the before and after values are maintained correctly.

@interface MySwitch : UISwitch

@end

@implementation MySwitch
{
    BOOL _previousValue;
    BOOL _returnPreviousValue;
}

- (instancetype) initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder: aDecoder];
    if (!self) return nil;

    _previousValue = self.isOn;
    [self addTarget: self action: @selector(_didChange)
                forControlEvents: UIControlEventValueChanged];

    return self;
}

- (instancetype) initWithFrame: (CGRect) frame
{
    self = [super initWithFrame: frame];
    if (!self) return nil;

    [self addTarget: self action: @selector(_didChange)
                forControlEvents: UIControlEventValueChanged];

    return self;
}

- (BOOL) isOn
{
    return (_returnPreviousValue)
                        ? _previousValue
                        : [super isOn];
}

- (void) setOn:(BOOL) on animated: (BOOL) animated
{
    [super setOn: on animated: animated];

    _previousValue = on;
}

- (void) _didChange
{
    BOOL isOn = self.isOn;

    if (isOn == _previousValue) return;

    _returnPreviousValue = true;
    [self willChangeValueForKey: @"on"];
    _returnPreviousValue = false;

    _previousValue = isOn;
    [self didChangeValueForKey:  @"on"];
}

@end

Upvotes: 1

nids
nids

Reputation: 73

When you toggle the switch off/On the "value changed" has been called.So you can detecting a change in switch by calling method on valueChanged.

Upvotes: 0

Erdemus
Erdemus

Reputation: 2788

Get state of switch in handler:

- (void)valueChanged:(UISwitch *)theSwitch {
   BOOL flag = theSwitch.on;
}

Upvotes: 8

antsyawn
antsyawn

Reputation: 981

Each press you make doesn't immediately toggle the switch on/off. If the switch is in the off position, you can get a couple of presses in before it animates to the on position. Each of these presses are interpreted as "turn the switch on", since it is not considered "on" until the animation has completed. You get a "valueChanged" callback for each press despite the fact that the value hasn't actually changed yet.

Upvotes: 7

Ben
Ben

Reputation: 2992

Log the last state so you can tell if its changed state or has been triggered with the same state.

Upvotes: 0

Related Questions