Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / iOS

iOS Tricks: Giving UISwitch's on Property Will/Did Change Observable Notifications

4.11/5 (2 votes)
15 Jul 2014CPOL 19.2K   24  
The UISwitch's UIControlEventValueChanged event is pretty useless if you'd like to get a notification only when the switch value actually changes.

Introduction

The UISwitch's UIControlEventValueChanged event is pretty useless if you'd like to get a notification only when the switch value actually changes. I have a solution that allows you to attach to the switch's "on" property's to get accurate change notifications.

Background

UISwitch inherits from UIControl which only has a UIControlEventValueChangedbefore value for you to determine if the value actually did change.

The More Better Class

The MySwitch class tweaks the UISwitch control by posting key/value change events when the control's value changes. It stores the previous value so it can determine when the on property actually changes.

MySwitch Interface

C++
@interface MySwitch : UISwitch
@end

MySwitch Implementation

C++
@implementation MySwitch
{
    BOOL _previousValue;
    BOOL _returnPreviousValue;
}

- (instancetype) initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder: aDecoder];
    if (!self) return nil;
    
    _previousValue = self.isOn; // if constructed via a nib
    [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) // see [_didChange]
                        ? _previousValue
                        : [super isOn];
}

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

    _previousValue = on; // if set manually
}

- (void) _didChange
{
    BOOL isOn = self.isOn;
    
    if (isOn == _previousValue) return;
    
    _returnPreviousValue = true; // the will change might query 
                      // the current value. (isOn must then return the previous value) 
    [self willChangeValueForKey: @"on"];
    _returnPreviousValue = false;
    
    _previousValue = isOn;
    [self didChangeValueForKey:  @"on"];
}

@end

And Then Make Your Codez Sweet

All you need to do is add an observer to the MySwitch instance. The observer can even get the old and new values in the change parameter. (See NSKeyValueObservingOptionOld and NSKeyValueObservingOptionNew)

C++
- (void) viewDidLoad
{
    [super viewDidLoad];

    [self.switchView addObserver: self forKeyPath: @"on" 
                         options: NSKeyValueObservingOptionNew context: nil];
}

- (void) observeValueForKeyPath: (NSString *) 
keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context
{
    if ([[change objectForKey: @"new"] boolValue])
    {
        self.textView.text = 
            [self.textView.text stringByAppendingString: @"changed to: ON\n"];
    }
    else
    {
        self.textView.text = 
            [self.textView.text stringByAppendingString: @"changed to: OFF\n"];
    }
}

- (void) toggleSwitch : (id) sender
{
    self.switchView.on = !self.switchView.on;
}

History

  • Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)