DKNightVersion夜間模式實(shí)現(xiàn)原理
DKNightVersion夜間模式實(shí)現(xiàn)的最基本原理:每一種類(lèi)型的控件注冊(cè)通知,當(dāng)用戶(hù)點(diǎn)擊夜間模式時(shí),發(fā)送通知,然后各控件響應(yīng)通知方法,更改顏色.
-
注冊(cè)通知,添加通知響應(yīng)方法.通過(guò)給根類(lèi)NSObject(必須是根類(lèi)里)添加類(lèi)別Night,在類(lèi)別里添加屬性pickers.初始化pickers時(shí),注冊(cè)通知.并在類(lèi)別里添加通知響應(yīng)方法
- (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 }]; }]; } -
注冊(cè)通知時(shí)機(jī).提供另外的設(shè)置背景色的方法
dk_setBackgroundColorPicker.使用之前的屬性self.pickers,保存背景色,self.pickers在初始化時(shí)會(huì)注冊(cè)通知.- (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:"]; } -
發(fā)送通知,更改主題.作者是通過(guò)提供一個(gè)
@property (nonatomic, strong) DKThemeVersion *themeVersion;themeVersion屬性,在設(shè)置的時(shí)候發(fā)送通知.- (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中有幾點(diǎn)是值得學(xué)習(xí)的:
- 作者并沒(méi)有在每個(gè)控件初始化的時(shí)候注冊(cè)通知.在每個(gè)控件初始化的時(shí)候注冊(cè)通知不僅繁瑣,而且沒(méi)辦法進(jìn)行封裝.所以必須找到一個(gè)總?cè)肟?在那里進(jìn)行通知的注冊(cè).
- 盡可能少的代碼侵入性,通過(guò)類(lèi)別可以實(shí)現(xiàn)最少的代碼侵入性.通過(guò)給根類(lèi)添加類(lèi)別,再通過(guò)runtime添加屬性.這樣就有了一個(gè)總?cè)肟?因?yàn)槊總€(gè)類(lèi)都是繼承自NSObject的.
- 作者將通知的注冊(cè)和通知的發(fā)送都封裝在設(shè)置屬性里,很好的隱藏了實(shí)現(xiàn)的細(xì)節(jié).