概述
DKNightVersion是github上面一個(gè)用于實(shí)現(xiàn)iOS應(yīng)用夜間模式和多種主題的開源庫角溃。github上面有兩個(gè)star數(shù)較高的庫,DKNightVersion和SwiftTheme。后者源碼是用swift實(shí)現(xiàn)的,OC和Swift混編導(dǎo)致應(yīng)用的體積大幅度增加,于是選擇了DKNightVersion喉誊。
使用方法
舉例說明,此處假設(shè)我們的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。
等等件余,能用別的方式來創(chuàng)建dk_XXXColorPicker嗎讥脐,比如RGB數(shù)值?當(dāng)然可以DKNightVersion提供了 DKColorPickerWithRGB(NSUInteger normal, ...)接口來根據(jù)色號生成dk_XXXColorPicker啼器。以上我們的Theme有n種旬渠,那么我們就需要在在dk_XXXColorPicker里面?zhèn)魅雗個(gè)代表顏色的參數(shù)。
也可以設(shè)置不同Theme下的圖片端壳。
DKImagePicker Examples
imageView.dk_image = DKImagePickerWithImages([UIImage imageNamed:@"white"], [UIImage imageNamed:@"black"]); // => imageView的image在日間模式告丢、夜間模式分別為圖片名為white、black代表的圖片损谦。
也有很多方法來生成dk_image,例如:DKImagePickersWithImageNames(@"white",@"black")岖免,直接根據(jù)圖片名來生成dk_image,等等照捡。
設(shè)置好了不同Theme下的顏色和圖片颅湘,如下代碼即可:
Theme Switch
[DKNightVersionManager sharedManager].themeVersion = DKThemeVersionNormal;// or DKThemeVersionNight => 將當(dāng)前的主題切換到日間模式或夜間模式。
實(shí)現(xiàn)思路
先看下上面的例子中用到的一些屬性栗精。
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;
從使用方法里面可以看到我們在設(shè)置給UI控件的DKColorPicker屬性賦值時(shí)闯参,傳入了n個(gè)顏色。n個(gè)顏色對應(yīng)了n中Theme,而且他們根據(jù)索引一一對應(yīng)赢赊。
DKNightVersionManager是一個(gè)用于管理主題的單例乙漓。當(dāng)[DKNightVersionManager sharedManager].themeVersion 發(fā)生改變時(shí),也就是當(dāng)前的Theme發(fā)生了個(gè)改變释移。會發(fā)一個(gè)通知告訴所有的設(shè)置過DKColorPicker的UI控件叭披。
UI控件收到通知后去找DKNightVersionManager去拿到當(dāng)前的Theme,根據(jù)Theme更新UI控件的相關(guān)屬性(backgroundColor玩讳,tintColor, textColor, image等)涩蜘,這邊是實(shí)現(xiàn)這個(gè)功能一個(gè)大體的思路。
DKColorPicker是什么熏纯?它并不是一個(gè)用來存color的數(shù)組同诫,它的定義是這樣的:
DKColorPicker,DKImagePicker
typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion);
typedef UIImage *(^DKImagePicker)(DKThemeVersion *themeVersion);
它是一個(gè)block樟澜,傳入一個(gè)我們已經(jīng)定義好了的Theme误窖,這個(gè)block給出一個(gè)color,用以更新秩贰。DKImagePicker同理霹俺。
每個(gè)對象都有一個(gè)pickers屬性
pickers property
@interface NSObject ()
@property (nonatomic, strong) NSMutableDictionary<NSString *, DKColorPicker> *pickers;
@end
在第一次使用這個(gè)屬性時(shí),當(dāng)前對象注冊為 DKNightVersionThemeChangingNotification 通知的觀察者毒费。pickers屬性只有在對象的某個(gè)DKColorPicker/DKImagePicker首次被賦值時(shí)才會被創(chuàng)建丙唧。
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:"];
}
當(dāng)Theme發(fā)生變化時(shí),DKNightVersionManager會發(fā)出通知觅玻,所有監(jiān)聽DKNightVersionThemeChangingNotification的對象調(diào)用night_update方法去更新色值和圖片想际。實(shí)現(xiàn)如下:
notification action
- (void)night_updateColor {
[self.pickers enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull selector, DKColorPicker _Nonnull picker, BOOL * _Nonnull stop) {
SEL sel = NSSelectorFromString(selector);
// picker根據(jù)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:,根據(jù)selector生成方法桩匪,然后去更新對象的顏色打瘪,圖片等。這個(gè)庫已經(jīng)包含了所有的原生UI控件的color和image屬性傻昙,通過runtime,category給UI控件添加屬性闺骚。
作者推薦我們使用如下的方式來創(chuàng)建DKColorPicker。在DKColorTable.txt中妆档,配置我們需要的色值和主題僻爽,內(nèi)容如下:
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分別對應(yīng)三個(gè)主題胸梆。
那么通過 DKColorPickerWithKey(BG)敦捧,生成對應(yīng)三個(gè)主題的DKColoPicker,并且目前的Theme只能通過修改DKColorTable.txt的文件內(nèi)容進(jìn)行管理碰镜。
總結(jié)
- 這個(gè)庫可以實(shí)現(xiàn)我們當(dāng)前的大多數(shù)的需求兢卵,目前這個(gè)庫還不能比較方便的解決富文本的不同主題的不同樣式問題,我們可以參照它的實(shí)現(xiàn)給需要使用富文本的控件添加DKNightVersionThemeChangingNotification監(jiān)聽绪颖,從而根據(jù)不同的Theme做出不同的展現(xiàn)秽荤,這個(gè)思路當(dāng)然也可以拓展到其他地方,雖然會造成比較強(qiáng)的耦合關(guān)系柠横,如不同主題下的不同樣式的展現(xiàn)等等窃款。
- 鑒于當(dāng)前Theme的管理方式,而且DKColorPicker用DKColorPickerWithKey()以外的其他方法創(chuàng)建傳入的數(shù)值并非動態(tài)的牍氛,所以以后增加Theme時(shí)可能會比較棘手晨继。作者不推薦我們手動創(chuàng)建DKColorPicker,而推薦使用DKColorTable.txt 來進(jìn)行主題管理, 這樣DKColorPicker中包含的色值由DKColorTable.txt中的配置決定搬俊,這樣會更加方便紊扬。
- 它不僅僅支持原生的backgroundColor,tintColor等屬性悠抹,可以給自定義的控件添加一個(gè)你想要的的color/image屬性珠月,例如pressedColor,在不同的主題做出不同的展現(xiàn)楔敌。
- 這個(gè)庫的一個(gè)比較好的實(shí)現(xiàn)我覺得是把Block當(dāng)做一個(gè)屬性賦值給對象而不是存儲一個(gè)數(shù)組或字典,然后根據(jù)其他變量的變化做出響應(yīng)的一個(gè)思路驻谆。
- 使用的時(shí)候我們自己新建一個(gè)theme_color_table.txt文件卵凑,然后在appdelegate調(diào)用
[[DKColorTable sharedColorTable] setFile:@"theme_color_table.txt"];
注意如果使用系統(tǒng)自帶的編輯軟件編輯theme_color_table.txt 的話,可能會出現(xiàn)空格導(dǎo)致崩潰而且一般看不出來胜臊,最好用專用的文字編輯軟件去添加新的配置勺卢。