皮肤切换

提供资讯、信息类的App一般都有白天和黑夜两种阅读模式

  • 现有项目中皮肤切换思路
    1、资源文件(图片、plist色板值、接口读取数据拼接html模版所使用到的样式表CSS等),都需准备两套。
    2、对用到的控件进行父类继承,扩展属性用字符串设置图片、文本颜色名称,如UIButton包括:未选中图片、高亮中图片、选中图片、禁用图片;未选中文字颜色、高亮中文字颜色、选中文字颜色、禁用文字颜色;未选中背景图片、高亮背景图片、选中背景图片、禁用背景图片等。
    3、皮肤切换,保存当前模式到本地:通过发送通知的方式,控件接收通知,通过工具类方法刷新重新读取该皮肤模式下对应的颜色或图片;web页面读取该皮肤模式下的样式表,通过JS替换CSS的href。
  • DKNightVersion皮肤切换学习
    1、目录结构:
tmp40972269.png

Core:核心类(DKColor颜色设置,DKImage图片设置,DKColorTable处理皮肤配置文件,DKNightVersionManager皮肤管理类,NSObject+Night扩展一个DKNightVersionManager)
DeallocBlockExecutor:内存回收(移除通知)相关的回调
CoreAnimation:动画Layer的扩展
Resources:皮肤配置文件
UIKit:皮肤控件的扩展
2、思路
2.1、扩展NSObject通过DKNightVersionManager单例来管理皮肤的切换,设置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:DKNightVersionThemeChangingNotificaiton
                                                        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
    }
}

2.2、颜色设置:如TableViewCell的背景颜色通过一个属性dk_cellTintColorPicker进行,实质是一个 block,它接收参数 DKThemeVersion *themeVersion,但是会返回一个 UIColor *
UIKit扩展中.m文件中的属性pickersNSObject+Nightpickers是一个东西。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
    // 项目中这里是写的一个宏自动生成,效果跟写一个UITableViewCell+Night类别是一样的
    cell.dk_cellTintColorPicker = DKColorPickerWithRGB(0xffffff, 0x343434, 0xfafafa);

    return cell;
}

2.3、然后通过属性关联设置UITableView的 tintColor;同时,每一个对象还持有一个pickers 数组,来存储自己的全部 DKColorPicker:

@interface UITableViewCell ()

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

@end

@implementation UITableViewCell (Night)

- (DKColorPicker)dk_cellTintColorPicker {
    return objc_getAssociatedObject(self, @selector(dk_cellTintColorPicker));
}

- (void)dk_setCellTintColorPicker:(DKColorPicker)picker {
    objc_setAssociatedObject(self, @selector(dk_cellTintColorPicker), picker, OBJC_ASSOCIATION_COPY_NONATOMIC);
    self.tintColor = picker(self.dk_manager.themeVersion);
    [self.pickers setValue:[picker copy] forKey:@"setTintColor:"];
}

@end

2.4、在第一次使用这个属性时,当前对象注册为 DKNightVersionThemeChangingNotificaiton 通知的观察者。 pickers属性只有在对象的某个 DKColorPicker/DKImagePicker首次被赋值时才会被创建。
在每次收到通知时,都会调用 night_update 方法,将当前主题传入 DKColorPicker,并再次执行,并将结果传入对应的属性 [self performSelector:sel withObject:result]

- (NSMutableDictionary<NSString *, DKColorPicker> *)pickers {
    // 第一次进来pickers为空进入if
    NSMutableDictionary<NSString *, DKColorPicker> *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];
        // 将局部变量pickers和当前对象的pickers关联
        objc_setAssociatedObject(self, @selector(pickers), pickers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        
        [[NSNotificationCenter defaultCenter] removeObserver:self name:DKNightVersionThemeChangingNotificaiton object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(night_updateColor) name:DKNightVersionThemeChangingNotificaiton 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
                         }];
    }];
}
  • objc_AssociationPolicy几种类型区别:Objective-C中的Associated Objects - 曾静的技术博客
    1、 OBJC_ASSOCIATION_ASSIGN,给关联对象指定弱引用,相当于 @property(assign)@property(unsafe_unretained)
    2、 OBJC_ASSOCIATION_RETAIN_NONATOMIC,给关联对象指定非原子的强引用,相当于 @property(nonatomic,strong)@property(nonatomic,retain)
    3、 OBJC_ASSOCIATION_COPY_NONATOMIC, 给关联对象指定非原子的copy特性,相当于 @property(nonatomic,copy)
    4、 OBJC_ASSOCIATION_RETAIN,给关联对象指定原子强引用,相当于 @property(atomic,strong)@property(atomic,retain)
    5、 OBJC_ASSOCIATION_COPY,给关联对象指定原子copy特性,相当于 @property(atomic,copy)

objc_setAssociatedObject:用来把一个对象与另一个对象进行关联。一共需要四个参数,分别是:源对象,关键字,关联的对象和一个关联策略。源对象和关联对象就是需要进行关联的两个对象; 关键字是一个void类型的指针, 每一个关联的关键字必须是唯一的,通常都是会采用静态变量来作为关键字,一般情况下也可以取@selector(function_name)即取得一个function的id作为关键字;关联策略是一个枚举,用来表示两个对象的关联程度。
objc_getAssociatedObject:和objc_setAssociatedObject配套使用,它是获取相关联的对象时使用的,objc_getAssociatedObject:两个参数,源对象、关键字(注意关键字唯一且一致)。

  • run Time在项目中的运用
    实质:是一个运行时库(Runtime Library),它是一个主要使用 C 和汇编写的库,为 C 添加了面相对象的能力并创造了 Objective-C。这就是说它在类信息(Class information) 中被加载,完成所有的方法分发,方法转发,等等。Objective-C runtime 创建了所有需要的结构体。Objective-C为什么有面相对象的能力?就是因为有runtime这个鬼东西!参考://www.greatytc.com/p/bba1ac264873

1、动态添加属性;
2、方法切换
云信消息处理:message的时间与处理(13位处理成10位)

+(void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 要特别注意你替换的方法到底是个性质的方法
        // When swizzling a Instance method, use the following:
        // Class class = [self class];

        // When swizzling a class method, use the following:
        Class class = object_getClass((id)self);

        SEL originalSelector = @selector(systemMethod_PrintLog);
        SEL swizzledSelector = @selector(ll_imageName);

        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

3、获取一个类的所有成员变量
获得某个类的所有成员变量 Ivar *class_copyIvarList(Class cls , unsigned int *outCount):(哪个类,放一个接收值的地址,用来存放属性的个数),返回值:存放所有获取到的属性
获得成员变量的名字 const char *ivar_getName(Ivar v)
获得成员变量的类型 const char *ivar_getTypeEndcoding(Ivar v)

unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([Person class], &outCount);
// 遍历所有成员变量
for (int i = 0; i < outCount; i++) { 
    // 取出i位置对应的成员变量 
    Ivar ivar = ivars[i]; 
    const char *name = ivar_getName(ivar); 
    const char *type = ivar_getTypeEncoding(ivar); 
    NSLog(@"成员变量名:%s 成员变量类型:%s",name,type);
}
// 注意释放内存!
free(ivars);
  • Objective C类方法load和initialize的区别
    1、load是只要类所在文件被引用就会被调用,而initialize是在类或者其子类的第一个方法被调用前调用。所以如果类没有被引用进项目,就不会有load调用;但即使类文件被引用进来,但是没有使用,那么initialize也不会被调用。
    2、相同点在于:方法只会被调用一次。

  • runtime 完整总结
    来自南峰子博客,就是对“objc/runtime.h”的解读

  • 查看Demo请点击
    使用plist文件进行色值配置

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,700评论 7 64
  • 吴蜀难降魏, 文武需两全。 东风成诸葛, 枭雄败赤壁。 望古今多少成败, 昨日风吹云散。 明争暗夺, 名利难收。 ...
    风中潇洒小青年阅读 67评论 0 1
  • 似梦 天蓝蓝 水盈盈 山青青 草油油 天上人间 非梦 微风轻抚湖面 花香悠远 欢快的鸟儿 吟唱心曲 走进一个梦 祈...
    舒洁湲阅读 285评论 15 4