DKNightVersion夜间模式实现原理

DKNightVersion夜间模式实现原理

DKNightVersion夜间模式实现的最基本原理:每一种类型的控件注册通知,当用户点击夜间模式时,发送通知,然后各控件响应通知方法,更改颜色.

  1. 注册通知,添加通知响应方法.通过给根类NSObject(必须是根类里)添加类别Night,在类别里添加属性pickers.初始化pickers时,注册通知.并在类别里添加通知响应方法- (void)night_updateColor.

    @implementation NSObject (Night)
    
    - (NSMutableDictionary *)pickers {
        NSMutableDictionary *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
                             }];
        }];
    }
    
    
  2. 注册通知时机.提供另外的设置背景色的方法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:"];
    }
    
  3. 发送通知,更改主题.作者是通过提供一个@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的.
  • 作者将通知的注册和通知的发送都封装在设置属性里,很好的隐藏了实现的细节.

你可能感兴趣的:(DKNightVersion夜间模式实现原理)