DKNightVersion夜间模式实现原理
DKNightVersion夜间模式实现的最基本原理:每一种类型的控件注册通知,当用户点击夜间模式时,发送通知,然后各控件响应通知方法,更改颜色.
-
注册通知,添加通知响应方法.通过给根类NSObject(必须是根类里)添加类别Night,在类别里添加属性pickers.初始化pickers时,注册通知.并在类别里添加通知响应方法
- (void)night_updateColor
.@implementation NSObject (Night) - (NSMutableDictionary<NSString *, DKColorPicker> *)pickers { NSMutableDictionary<NSString *, DKColorPicker> *pickers = objc_getAssociatedObject(self, @selector(pickers)); if (!pickers) { @autoreleasepool { // Need to removeObserver in dealloc if (objc_getAssociatedObject(self, &DKViewDeallocHelperKey) == nil) { __unsafe_unretained typeof(self) weakSelf = self; // NOTE: need to be __unsafe_unretained because __weak var will be reset to nil in dealloc id deallocHelper = [self addDeallocBlock:^{ [[NSNotificationCenter defaultCenter] removeObserver:weakSelf]; }]; objc_setAssociatedObject(self, &DKViewDeallocHelperKey, deallocHelper, OBJC_ASSOCIATION_ASSIGN); } } pickers = [[NSMutableDictionary alloc] init]; objc_setAssociatedObject(self, @selector(pickers), pickers, OBJC_ASSOCIATION_RETAIN_NONATOMIC); [[NSNotificationCenter defaultCenter] removeObserver:self name:DKNightVersionThemeChangingNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(night_updateColor) name:DKNightVersionThemeChangingNotification object:nil]; } return pickers; } - (void)night_updateColor { [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker _Nonnull picker, BOOL * _Nonnull stop) { SEL sel = NSSelectorFromString(selector); id result = picker(self.dk_manager.themeVersion); [UIView animateWithDuration:DKNightVersionAnimationDuration animations:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:sel withObject:result]; #pragma clang diagnostic pop }]; }]; }
-
注册通知时机.提供另外的设置背景色的方法
dk_setBackgroundColorPicker
.使用之前的属性self.pickers
,保存背景色,self.pickers
在初始化时会注册通知.- (void)dk_setBackgroundColorPicker:(DKColorPicker)picker { objc_setAssociatedObject(self, @selector(dk_backgroundColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); self.backgroundColor = picker(self.dk_manager.themeVersion); [self.pickers setValue:[picker copy] forKey:@"setBackgroundColor:"]; }
-
发送通知,更改主题.作者是通过提供一个
@property (nonatomic, strong) DKThemeVersion *themeVersion;
themeVersion属性,在设置的时候发送通知.- (void)setThemeVersion:(DKThemeVersion *)themeVersion { if ([_themeVersion isEqualToString:themeVersion]) { // if type does not change, don't execute code below to enhance performance. return; } _themeVersion = themeVersion; // Save current theme version to user default [[NSUserDefaults standardUserDefaults] setValue:themeVersion forKey:DKNightVersionCurrentThemeVersionKey]; [[NSNotificationCenter defaultCenter] postNotificationName:DKNightVersionThemeChangingNotification object:nil]; if (self.shouldChangeStatusBar) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" if ([themeVersion isEqualToString:DKThemeVersionNight]) { [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; } else { [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; } #pragma clang diagnostic pop }
}
```
DKNightVersion中有几点是值得学习的:
- 作者并没有在每个控件初始化的时候注册通知.在每个控件初始化的时候注册通知不仅繁琐,而且没办法进行封装.所以必须找到一个总入口,在那里进行通知的注册.
- 尽可能少的代码侵入性,通过类别可以实现最少的代码侵入性.通过给根类添加类别,再通过runtime添加属性.这样就有了一个总入口,因为每个类都是继承自NSObject的.
- 作者将通知的注册和通知的发送都封装在设置属性里,很好的隐藏了实现的细节.