使用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当做一个属性赋值给对象而不是存储一个数组或字典,然后根据其他变量的变化做出响应的一个思路。
  • 使用的时候我们自己新建一个theme_color_table.txt文件,然后在appdelegate调用[[DKColorTable sharedColorTable] setFile:@"theme_color_table.txt"]; 注意如果使用系统自带的编辑软件编辑theme_color_table.txt 的话,可能会出现空格导致崩溃而且一般看不出来,最好用专用的文字编辑软件去添加新的配置。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容