diff options
Diffstat (limited to 'Framework/MASShortcutView+UserDefaults.m')
| -rw-r--r-- | Framework/MASShortcutView+UserDefaults.m | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/Framework/MASShortcutView+UserDefaults.m b/Framework/MASShortcutView+UserDefaults.m new file mode 100644 index 0000000..a84f0c9 --- /dev/null +++ b/Framework/MASShortcutView+UserDefaults.m @@ -0,0 +1,125 @@ +#import "MASShortcutView+UserDefaults.h" +#import "MASShortcut.h" +#import <objc/runtime.h> + +@interface MASShortcutDefaultsObserver : NSObject + +@property (nonatomic, readonly) NSString *userDefaultsKey; +@property (nonatomic, readonly, weak) MASShortcutView *shortcutView; + +- (id)initWithShortcutView:(MASShortcutView *)shortcutView userDefaultsKey:(NSString *)userDefaultsKey; + +@end + +#pragma mark - + +@implementation MASShortcutView (UserDefaults) + +void *MASAssociatedDefaultsObserver = &MASAssociatedDefaultsObserver; + +- (NSString *)associatedUserDefaultsKey +{ + MASShortcutDefaultsObserver *defaultsObserver = objc_getAssociatedObject(self, MASAssociatedDefaultsObserver); + return defaultsObserver.userDefaultsKey; +} + +- (void)setAssociatedUserDefaultsKey:(NSString *)associatedUserDefaultsKey +{ + // First, stop observing previous shortcut view + objc_setAssociatedObject(self, MASAssociatedDefaultsObserver, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); + + if (associatedUserDefaultsKey.length == 0) return; + + // Next, start observing current shortcut view + MASShortcutDefaultsObserver *defaultsObserver = [[MASShortcutDefaultsObserver alloc] initWithShortcutView:self userDefaultsKey:associatedUserDefaultsKey]; + objc_setAssociatedObject(self, MASAssociatedDefaultsObserver, defaultsObserver, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +@end + +#pragma mark - + +@implementation MASShortcutDefaultsObserver { + MASShortcut *_originalShortcut; + BOOL _internalPreferenceChange; + BOOL _internalShortcutChange; +} + +- (id)initWithShortcutView:(MASShortcutView *)shortcutView userDefaultsKey:(NSString *)userDefaultsKey +{ + self = [super init]; + if (self) { + _originalShortcut = shortcutView.shortcutValue; + _shortcutView = shortcutView; + _userDefaultsKey = userDefaultsKey.copy; + [self startObservingShortcutView]; + } + return self; +} + +- (void)dealloc +{ + // __weak _shortcutView is not yet deallocated because it refers MASShortcutDefaultsObserver + [self stopObservingShortcutView]; +} + +#pragma mark - + +void *kShortcutValueObserver = &kShortcutValueObserver; + +- (void)startObservingShortcutView +{ + // Read initial shortcut value from user preferences + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + NSData *data = [defaults dataForKey:_userDefaultsKey]; + _shortcutView.shortcutValue = [MASShortcut shortcutWithData:data]; + + // Observe user preferences to update shortcut value when it changed + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:defaults]; + + // Observe the keyboard shortcut that user inputs by hand + [_shortcutView addObserver:self forKeyPath:@"shortcutValue" options:0 context:kShortcutValueObserver]; +} + +- (void)userDefaultsDidChange:(NSNotification *)note +{ + // Ignore notifications posted from -[self observeValueForKeyPath:] + if (_internalPreferenceChange) return; + + _internalShortcutChange = YES; + NSData *data = [note.object dataForKey:_userDefaultsKey]; + _shortcutView.shortcutValue = [MASShortcut shortcutWithData:data]; + _internalShortcutChange = NO; +} + +- (void)stopObservingShortcutView +{ + // Stop observing keyboard hotkeys entered by user in the shortcut view + [_shortcutView removeObserver:self forKeyPath:@"shortcutValue" context:kShortcutValueObserver]; + + // Stop observing user preferences + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSUserDefaultsDidChangeNotification object:[NSUserDefaults standardUserDefaults]]; + + // Restore original hotkey in the shortcut view + _shortcutView.shortcutValue = _originalShortcut; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context +{ + if (context == kShortcutValueObserver) { + if (_internalShortcutChange) return; + MASShortcut *shortcut = [object valueForKey:keyPath]; + _internalPreferenceChange = YES; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:(shortcut.data ?: [NSKeyedArchiver archivedDataWithRootObject:nil]) forKey:_userDefaultsKey]; + [defaults synchronize]; + + _internalPreferenceChange = NO; + } + else { + [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; + } +} + +@end |
