天天看點

使用DKNightVersion實作夜間模式

概述

DKNightVersion是github上面一個用于實作iOS應用夜間模式和多種主題的開源庫。github上面有兩個star數較高的庫,DKNightVersion和SwiftTheme。後者源碼是用swift實作的,OC和Swift混編導緻應用的體積大幅度增加,于是選擇了DKNightVersion。

使用方法

舉例說明,此處假設我們的Theme隻有兩種:普通模式,夜間模式。

DKColorPicker Examples
view.dk_backgroundColorPicker = DKColorPickerWithColors([UIColor whiteColor], [UIColor darkGrayColor]); // => view的backgroundColor在普通模式、夜間模式下分别為white、darkGray。
label.dk_textColorPicker = DKColorPickerWithColors([UIColor whiteColor], [UIColor darkGrayColor]);      // => label的textColor在普通模式、夜間模式下分别為white、darkGray。
tabBar.dk_barTintColorPicker = DKColorPickerWithColors([UIColor whiteColor], [UIColor darkGrayColor]);  // => tabBar的barTintColor在普通模式、夜間模式下分别為white、darkGray。
           

等等,能用别的方式來建立dk_XXXColorPicker嗎,比如RGB數值?當然可以DKNightVersion提供了 DKColorPickerWithRGB(NSUInteger normal, ...)接口來根據色号生成dk_XXXColorPicker。以上我們的Theme有n種,那麼我們就需要在在dk_XXXColorPicker裡面傳入n個代表顔色的參數。

也可以設定不同Theme下的圖檔。

DKImagePicker Examples
imageView.dk_image = DKImagePickerWithImages([UIImage imageNamed:@"white"], [UIImage imageNamed:@"black"]); // => imageView的image在普通模式、夜間模式分别為圖檔名為white、black代表的圖檔。
           

也有很多方法來生成dk_image,例如:DKImagePickersWithImageNames(@"white",@"black"),直接根據圖檔名來生成dk_image,等等。

設定好了不同Theme下的顔色和圖檔,如下代碼即可:

Theme Switch
[DKNightVersionManager sharedManager].themeVersion = DKThemeVersionNormal;// or DKThemeVersionNight => 将目前的主題切換到普通模式或夜間模式。                

實作思路

先看下上面的例子中用到的一些屬性。

UIView+night
// DKColorPicker definition
@property (nonatomic, copy, setter = dk_setBackgroundColorPicker:) DKColorPicker dk_backgroundColorPicker;
// DKImagePicker definition
@property (nonatomic, copy, setter = dk_setTintColorPicker:) DKColorPicker dk_tintColorPicker;
           
UIImageView+night
@property (nullable, nonatomic, copy, setter = dk_setImagePicker:) DKImagePicker dk_imagePicker;
           

從使用方法裡面可以看到我們在設定給UI控件的DKColorPicker屬性指派時,傳入了n個顔色。n個顔色對應了n中Theme,而且他們根據索引一一對應。

DKNightVersionManager是一個用于管理主題的單例。當[DKNightVersionManager sharedManager].themeVersion 發生改變時,也就是目前的Theme發生了個改變。會發一個通知告訴所有的設定過DKColorPicker的UI控件。

UI控件收到通知後去找DKNightVersionManager去拿到目前的Theme,根據Theme更新UI控件的相關屬性(backgroundColor,tintColor, textColor, image等),這邊是實作這個功能一個大體的思路。

DKColorPicker是什麼?它并不是一個用來存color的數組,它的定義是這樣的:

DKColorPicker,DKImagePicker
typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion); 
typedef UIImage *(^DKImagePicker)(DKThemeVersion *themeVersion);
           

它是一個block,傳入一個我們已經定義好了的Theme,這個block給出一個color,用以更新。DKImagePicker同理。

每個對象都有一個pickers屬性

pickers property
@interface NSObject ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, DKColorPicker> *pickers;
@end
           

在第一次使用這個屬性時,目前對象注冊為 DKNightVersionThemeChangingNotification 通知的觀察者。pickers屬性隻有在對象的某個DKColorPicker/DKImagePicker首次被指派時才會被建立。

dk_backgroundColorPicker setter
- (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:"];
}
           

當Theme發生變化時,DKNightVersionManager會發出通知,所有監聽DKNightVersionThemeChangingNotification的對象調用night_update方法去更新色值和圖檔。實作如下:

notification action
- (void)night_updateColor {
    [self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker  _Nonnull picker, BOOL * _Nonnull stop) {
        SEL sel = NSSelectorFromString(selector);
        // picker根據Theme拿到color/image值
        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_backgroundColor的setter方法中可以知道,上面的selector一般為setBackgroundColor:,setTintColor;,setImage:,根據selector生成方法,然後去更新對象的顔色,圖檔等。這個庫已經包含了所有的原生UI控件的color和image屬性,通過runtime,category給UI控件添加屬性。

作者推薦我們使用如下的方式來建立DKColorPicker。在DKColorTable.txt中,配置我們需要的色值和主題,内容如下:

NORMAL   NIGHT    RED
#ffffff  #343434  #fafafa BG
#aaaaaa  #313131  #aaaaaa SEP
#0000ff  #ffffff  #fa0000 TINT
#000000  #ffffff  #000000 TEXT
#ffffff  #444444  #ffffff BAR
#f0f0f0  #222222  #dedede HIGHLIGHTED                

NORMAL 、NIGHT、RED分别對應三個主題。

那麼通過 DKColorPickerWithKey(BG),生成對應三個主題的DKColoPicker,并且目前的Theme隻能通過修改DKColorTable.txt的檔案内容進行管理。

總結

  • 這個庫可以實作我們目前的大多數的需求,目前這個庫還不能比較友善的解決富文本的不同主題的不同樣式問題,我們可以參照它的實作給需要使用富文本的控件添加DKNightVersionThemeChangingNotification監聽,進而根據不同的Theme做出不同的展現,這個思路當然也可以拓展到其他地方,雖然會造成比較強的耦合關系,如不同主題下的不同樣式的展現等等。
  • 鑒于目前Theme的管理方式,而且DKColorPicker用DKColorPickerWithKey()以外的其他方法建立傳入的數值并非動态的,是以以後增加Theme時可能會比較棘手。作者不推薦我們手動建立DKColorPicker,而推薦使用DKColorTable.txt 來進行主題管理, 這樣DKColorPicker中包含的色值由DKColorTable.txt中的配置決定,這樣會更加友善。
  • 它不僅僅支援原生的backgroundColor,tintColor等屬性,可以給自定義的控件添加一個你想要的的color/image屬性,例如pressedColor,在不同的主題做出不同的展現。
  • 這個庫的一個比較好的實作我覺得是把Block當做一個屬性指派給對象而不是存儲一個數組或字典,然後根據其他變量的變化做出響應的一個思路。

文/斷腸人在摸蝦(簡書作者)

原文連結:http://www.jianshu.com/p/bae45500366b

著作權歸作者所有,轉載請聯系作者獲得授權,并标注“簡書作者”。

繼續閱讀