DKNightVersion框架的原理和使用

来源:互联网 发布:python初学者的书籍 编辑:程序博客网 时间:2024/05/19 19:15


DKNightVersion下载地址: https://github.com/Draveness/DKNightVersion

在很多重阅读或者需要在夜间观看的软件其实都会把夜间模式当做一个 App 所需要具备的特性. 而如何在不改变原有的架构, 甚至不改变原有的代码的基础上, 就能为应用优雅地添加夜间模式就成为一个在很多应用开发的过程中不得不面对的一个问题.

就是以上事情的驱动, 使我思考如何才能使用一种优雅并且简洁的方法解决这一问题.

而  DKNightVersion 就是我带来的解决方案. 

到目前为止, 这个框架的大部分的工作都已经完成了, 或许它现在不够完善, 不过我会持续地维护这个框架, 帮助饱受实现夜间模式之苦的工程师们解决这个

坑的一逼的

需求.

实现

现在我也终于有时间来

水一水

写一篇博客来说一下这个框架是如何实现夜间模式的, 它都有哪些特性.

在很长的一段时间我都在想如何才能在不覆写  UIKit 控件的基础上, 为 iOS App 添加夜间模式. 而  objc/runtime 为我带来了不覆写  UIKit 就能实现这一目的的希望. 

为 UIKit 控件添加  nightColor 属性 

因为我们并不会子类化 UIKit 控件, 然后使用  @property 为它的子类添加属性. 而是使用 Objective-C 中神奇的分类(Category) 和  objc/runtime , 为 UI 系列的控件添加属性. 

使用  objc/runtime 为分类添加属性相信很多人都知道而且经常在开发中使用了. 如果不了解的话, 可以看  这里 . 

DKNightVersion 为大多数常用的  color 比如说:  backgroundColor tintColor 都添加了以  night 开头的夜间模式下的颜色,  nightBackgroundColor nightTintColor . 

- (UIColor *)nightBackgroundColor {return objc_getAssociatedObject(self, &nightBackgroundColorKey) ? :self.backgroundColor);}- (void)setNightBackgroundColor:(UIColor *)nightBackgroundColor {objc_setAssociatedObject(self, &nightBackgroundColorKey, nightBackgroundColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}

我们创建这个属性以保存夜间模式下的颜色, 这样当应用的主题切换到夜间模式时, 将  nightColor 属性存储的颜色赋值给对应的  color , 但是这会有一个问题. 当应用重新切换回正常模式时, 我们失去了原有正常模式的  color . 

添加  normalColor 存储颜色 

为了解决这一问题, 我们为 UIKit 控件添加了另一个属性  normalColor 来保存正常模式下的颜色. 

- (UIColor *)normalBackgroundColor {return objc_getAssociatedObject(self, &normalBackgroundColorKey);}- (void)setNormalBackgroundColor:(UIColor *)normalBackgroundColor {objc_setAssociatedObject(self, &normalBackgroundColorKey, normalBackgroundColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}

但是保存这个颜色的时机是非常重要的, 在最开始的时候, 我的选择是直接覆写  setter 方法, 在保存颜色之前存储  normalColor . 

- (void)setBackgroundColor:(UIColor *)backgroundColor {    self.normalBackgroundColor = backgroundColor;    _backgroundColor = backgroundColor;}

然而这种看似可以运行的  setter 其实会导致视图不会被着色, 设置  color 包括正常的颜色都不会有任何的反应, 反而视图的背景颜色一片漆黑. 

由于上面这种方法行不通, 我想换一种方法使用观察者模式来存储  normalColor , 将实例自己注册为  color 属性的观察者, 当  color 属性变化时, 通知 UIKit 控件本身, 然后, 把属性存到  normalColor 属性中. 

然而在什么时候将自己注册为观察者这一问题, 又使我放弃了这一解决方案. 最终选择  方法调剂 来解决原有  color 的存储问题. 

使用方法调剂为原有属性的  setter 方法添加钩子, 在方法调用之前, 将属性存储起来, 用于切换回  normal 模式时, 为属性赋值. 

这是要与  setter 调剂的钩子方法: 

- (void)hook_setBackgroundColor:(UIColor*)backgroundColor {    if ([DKNightVersionManager currentThemeVersion] == DKThemeVersionNormal) {        [self setNormalBackgroundColor:backgroundColor];    }    [self hook_setBackgroundColor:backgroundColor];}

如果当前是  normal 模式, 就会存储  color , 如果不是就会直接赋值, 如果你看不懂为什么这里好像会造成无限递归, 请看  这里 , 详细的解释了方法调剂是如何使用的. 

DKNightVersionManager 实现  color 切换 

我们已经为 UIKit 控件添加了  normalColor 和  nightColor , 接下来我们需要实现  color 在这两者之间的切换, 而这  DKNightVersionManager 就是为了处理模式切换的类. 

通过为  DKNightVersionManager 创建一个单例来处理  模式转换 ,  使用默认颜色 ,  动画时间 等操作. 

当调用  DKNightVersionManager 的类方法  nightFalling 或者  dawnComing 时, 我们首先会获取全局的  UIWindow , 然后通过递归调用  changeColor 方法, 使能够响应  changeColor 方法的视图改变颜色. 

- (void)changeColor:(id <DKNightVersionChangeColorProtocol>)object {if ([object respondsToSelector:@selector(changeColor)]) {[object changeColor];}if ([object respondsToSelector:@selector(subviews)]) {if (![object subviews]) {// Basic case, do nothing.return;} else {for (id subview in [object subviews]) {// recursice darken all the subviews of current view.[self changeColor:subview];if ([subview respondsToSelector:@selector(changeColor)]) {[subview changeColor];}}}}}

因为我在这个类中并没有引入  category , 编译器不知道  id 类型具有这两个方法. 所以我声明了一个协议, 使  changeColor 中的方法来满足两个方法  changeColor 和  subViews . 不让编译器提示错误. 

@protocol DKNightVersionChangeColorProtocol <NSObject>- (void)changeColor;- (NSArray *)subviews;@end

然后让所有的 UIKit 控件遵循这个协议就可以了, 当然我们也可以不显示的遵循这个协议, 只要它能够响应这两个方法也是可以的.

实现默认颜色

我们要在 DKNightVersion 实现默认的夜间模式配色, 以便减少开发者的工作量.

但是因为我们对每种  color 只在父类中实现一次, 这样使得子类能够继承父类的实现, 但是同样 不想让 UIKit 系子类继承父类的默认颜色 . 

- (UIColor *)defaultNightBackgroundColor {BOOL notUIKitSubclass = [self isKindOfClass:[UIView class]] && ![NSStringFromClass(self.class) hasPrefix:@"UI"]; if ([self isMemberOfClass:[UIView class]] || notUIKitSubclass) { return UIColorFromRGB(0x343434);} else {UIColor *resultColor = self.normalBackgroundColor ?: [UIColor clearColor];return resultColor;}}

通过使用  isMemberOfClass: 方法来判断  实例是不是当前类的实例, 而不是该类子类的实例. 然后才会返回默认的颜色. 但是非 UIKit 中的子类是可以继承这个特性的, 所以使用这段代码来判断该实例是否是非 UIKit 的子类: 

[self isKindOfClass:[UIView class]] && ![NSStringFromClass(self.class) hasPrefix:@"UI"]

我们通过  NSStringFromClass(self.class) hasPrefix:@"UI" 巧妙地达到这一目的. 

使用  erb 生成 Objective-C 代码 

这个框架大多数的工作都是重复的, 但是我并不想为每一个类重复编写近乎相同的代码, 这样的代码十分不易阅读和维护, 所以使用了  erb 文件, 来为生成的 Objective-C 代码提供模板, 只将原数据进行解析然后传入每一个模板, 动态生成所有的代码, 再通过另一个脚本将所有的文件加入目录中. 

DKNightVersion 的实现并不复杂. 它不仅使用了  erb 和 Ruby 脚本来减少了大量的工作量, 而且使用了  objc/runtime 的特性来魔改 UIKit 组件, 达到为 iOS 应用添加夜间模式的效果.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

概述

DKNightVersion是github上面一个用于实现iOS应用夜间模式和多种主题的开源库。github上面有两个star数较高的库,DKNightVersion和SwiftTheme。后者源码是用Swift实现的,OC和Swift混编导致应用的体积大幅度增加,于是选择了DKNightVersion。

使用方法

举例说明,此处假设我们的Theme只有两种:普通模式,夜间模式。

DKColorPicker Examplesview.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 ExamplesimageView.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,DKImagePickertypedef 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当做一个属性赋值给对象而不是存储一个数组或字典,然后根据其他变量的变化做出响应的一个思路。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

补充:

下面是简单使用代码

#import "ViewController.h"#import <DKNightVersion/DKNightVersion.h>@interface ViewController ()@property (weak, nonatomic) IBOutlet UISwitch *switchs;@property (weak, nonatomic) IBOutlet UIButton *button;@property (weak, nonatomic) IBOutlet UIBarButtonItem *leftButton;@property (weak, nonatomic) IBOutlet UIBarButtonItem *rightButton;@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    [self changeColor];}// 修改颜色也可以通过设置修改,改成你自己喜换的颜色,在这我是使用了DKNightVersion封装好的- (void)changeColor{    UILabel *navLabel = [[UILabel alloc] init];    navLabel.text = @"夜间模式";    [navLabel sizeToFit];    self.navigationItem.titleView = navLabel;    // view的背景颜色    self.view.dk_backgroundColorPicker = DKColorPickerWithKey(BG);    // titleView颜色    navigationLabel.dk_textColorPicker = DKColorPickerWithKey(TEXT);    // 按钮title颜色    [self.button dk_setTitleColorPicker:DKColorPickerWithKey(TINT) forState:UIControlStateNormal];    // 开关颜色    self.switchs.dk_tintColorPicker = DKColorPickerWithKey(TINT);    // 左右item颜色    self.leftButton.dk_tintColorPicker = DKColorPickerWithKey(TINT);    self.rightButton.dk_tintColorPicker = DKColorPickerWithKey(TINT);}// button点击事件- (IBAction)open:(id)sender{    // 判断当前是否为夜间模式    if ([self.dk_manager.themeVersion isEqualToString:DKThemeVersionNight]) {        // 切换为白天模式        [self.dk_manager dawnComing];    } else {        // 切换为夜间模式        [self.dk_manager nightFalling];    }}// 监听switch开/关- (IBAction)change:(id)sender{    if (self.switchs.isOn) {        // 打开切换为白天模式        self.dk_manager.themeVersion = DKThemeVersionNight;    }else {        // 关闭切换为夜间模式        self.dk_manager.themeVersion = DKThemeVersionNormal;    }}// 左边item点击事件- (IBAction)left:(id)sender{    self.dk_manager.themeVersion = @"RED";}// 右边边item点击事件- (IBAction)right:(id)sender{    self.dk_manager.themeVersion = DKThemeVersionNormal;}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


DKColorPicker

与上一个版本实现上的不同,在 2.0 中删除了全部的 nightBackgroundColor,使用一个名为 dk_backgroundColorPicker 的属性取代它。

@property (nonatomiccopy) DKColorPicker dk_backgroundColorPicker;

这个属性其实就是一个 block,它接收参数 DKThemeVersion *themeVersion,但是会返回一个 UIColor *

在第一次传入 picker 或者每次主题改变时,都会将当前主题 DKThemeVersion 传入 picker 并执行,然后,将得到的 UIColor 赋值给对应的属性 backgroundColor 更新视图颜色。

typedef UIColor *(^DKColorPicker)(DKThemeVersion *themeVersion);

比如下面使用 DKColorPickerWithRGB 创建一个临时的 DKColorPicker

  1. 在 DKThemeVersionNormal 时返回 0xffffff

  2. 在 DKThemeVersionNight 时返回 0x343434

  3. 在自定义的主题下返回 0xfafafa (这里的顺序与色表中主题的顺序有关)

cell.dk_backgroundColorPicker = DKColorPickerWithRGB(0xffffff0x3434340xfafafa);

同时,每一个对象还持有一个 pickers 数组,来存储自己的全部 DKColorPicker

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

在第一次使用这个属性时,当前对象注册为 DKNightVersionThemeChangingNotificaiton 通知的观察者。

在每次收到通知时,都会调用 night_update 方法,将当前主题传入 DKColorPicker,并再次执行,并将结果传入对应的属性 [self performSelector:sel withObject:result]

- (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                         }];    }];}

也就是说,在每次改变主题的时候,都会发出通知。

DKColorTable

虽然我们在上面临时创建了一些 DKColorPicker。不过在 DKNightVersion 中,我更推荐使用色表,来减少相同的 DKColorPicker 的创建,并且能够更好地管理整个应用中的颜色:

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

上面就是默认色表文件 DKColorTable.txt 中的内容,其中,第一行表示主题,NORMAL 主题必须存在,而且必须为第一列,而最右面的 BGSEP 就是对应 DKColorPicker 的 key。

self.tableView.dk_backgroundColorPicker = DKColorPickerWithKey(BG);

在使用时,上面的代码就相当于返回了一个在 NORMAL 时返回 #ffffffNIGHT 时返回 #343434 以及 RED 时返回 #fafafa 的 DKColorPicker

pickerify

虽然说,我们使用色表以及 DKColorPicker 解决了,但是,到目前为止我们还没有解决第三方框架的问题。

比如我们使用了某个第三方框架,或者自己添加了某个 color 属性,比如说:

@interface DKView ()@property (nonatomic, strong) UIColor *weirdColor;@end

weirdColor 并没有对应的 DKColorPicker,但是,我们可以通过 pickerify 在想要使用 dk_weirdColorPicker 的地方生成这个对应的 picker:

@pickerify(DKView, weirdColor);

然后,我们就可以使用 dk_weirdColorPicker 属性了:

view.dk_weirdColorPicker = DKColorPickerWithKey(BG);

pickerify 其实是一个宏:

#define pickerify(KLASS, PROPERTY) interface \    KLASS (Night) \    @property (nonatomic, copy, setter = dk_set ## PROPERTY ## Picker:) DKColorPicker dk_ ## PROPERTY ## Picker; \    @end \    @interface \    KLASS () \    @property (nonatomic, strong) NSMutableDictionary<NSString *, DKColorPicker> *pickers; \    @end \    @implementation \    KLASS (Night) \    - (DKColorPicker)dk_ ## PROPERTY ## Picker { \        return objc_getAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker)); \    } \    - (void)dk_set ## PROPERTY ## Picker:(DKColorPicker)picker { \        objc_setAssociatedObject(self, @selector(dk_ ## PROPERTY ## Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC); \        [self setValue:picker(self.dk_manager.themeVersion) forKeyPath:@keypath(self, PROPERTY)];\        [self.pickers setValue:[picker copy] forKey:_DKSetterWithPROPERTYerty(@#PROPERTY)]; \    } \    @end

这个宏根据传入的类和属性名,为我们生成了对应 picker 的存取方法,它也可以说是一种元编程的手段。

这里生成的 setter 方法不是标准意义上的驼峰命名法 dk_setweirdColorPicker:,因为我不知道怎么才能让大写首字母之后的属性添加到这里(如果各位读者有解决方案,欢迎提 PR 或者 issue)。

嵌入式 Ruby

由于框架中很多的代码,都是重复的,所以在这里使用了嵌入式 Ruby 模板来生成对应的文件 color.m.irb

////  <%= klass.name %>+Night.m//  <%= klass.name %>+Night////  Copyright (c) 2015 Draveness. All rights reserved.////  These files are generated by ruby script, if you want to modify code//  in this file, you are supposed to update the ruby code, run it and//  test it. And finally open a pull request.#import "<%= klass.name %>+Night.h"#import "DKNightVersionManager.h"#import <objc/runtime.h>@interface <%= klass.name %> ()@property (nonatomic, strong) NSMutableDictionary<NSString *, DKColorPicker> *pickers;@end@implementation <%= klass.name %> (Night)<% klass.properties.each do |property| %><%= """- (DKColorPicker)dk_#{property.name}Picker {    return objc_getAssociatedObject(self, @selector(dk_#{property.name}Picker));}- (void)dk_set#{property.cap_name}Picker:(DKColorPicker)picker {    objc_setAssociatedObject(self, @selector(dk_#{property.name}Picker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC);    self.#{property.name} = picker(self.dk_manager.themeVersion);    [self.pickers setValue:[picker copy] forKey:@\"#{property.setter}\"];}""" %><% end %>@end

这部分的实现并不在这篇文章的讨论范围之内,如果,对这部分看兴趣,可以看一下仓库中的 generator 文件夹,其中包含了代码生成器的全部代码。

小结

如果你对 DKNightVersion 的使用有兴趣,可以查看仓库的 README 文件,有人会说不要在项目中 ObjC runtime,我个人觉得是没有问题,AFNetworking、 BlocksKit 也使用方法调剂来改变原有方法的实现,不能因为它强大就不使用它;正相反,有时候,使用 runtime 才能优雅地解决问题。


部分代码:

 if (theSwitch.on ==YES) {//换至夜间模式

            self.dk_manager.themeVersion =DKThemeVersionNight;

            self.tabBarController.tabBar.barTintColor = [UIColor colorWithRed:34/255.0green:34/255.0blue:34/255.0alpha:1.0];


        } else { //普通模式

            self.dk_manager.themeVersion =DKThemeVersionNormal;

            self.tabBarController.tabBar.barTintColor = [UIColor whiteColor];


        }


////////////////////////////////////////////////////////////////////////////////

 UserInfoCell *cell = [tableViewdequeueReusableCellWithIdentifier:UserInfoCellIdentifier];

        cell.textLabel.dk_textColorPicker =DKColorPickerWithKey(TEXT);


////////////////////////////////////////////////////////////////////////////////

SwitchCell *cell = [tableViewdequeueReusableCellWithIdentifier:SwitchCellIdentifier];

        cell.dk_backgroundColorPicker =DKColorPickerWithRGB(0xffffff,0x343434,0xfafafa);


////////////////////////////////////////////////////////////////////////////////

self.tableView.dk_backgroundColorPicker =DKColorPickerWithRGB(0xf0f0f0,0x000000,0xfafafa);

    self.tableView.dk_separatorColorPicker = DKColorPickerWithKey(SEP);

    self.navigationController.navigationBar.dk_barTintColorPicker = DKColorPickerWithRGB(0xfa5054,0x444444,0xfa5054);





0 0
原创粉丝点击