转自:https://www.ianisme.com的博客

一、前言:

tip: 本来这篇文章在圣诞节就已经准备好了,但是由于种种原因一直没有写完,今天将它写出来,也算是2018年的第一篇文章了。你好,2018!

过去圣诞节是各大APP浓妆艳抹展现自己衣服的节日,今年的圣诞节似乎冷清了许多,只看到了几个APP换肤,那我就从中分析一下吧。

二、分析:

我认为目前的换肤主要分成3种,一种是返回图片的地址,APP再根据图片日志去取图片,另一种是下载zip包然后再解压去替换图标,再一种是图片资源放到包里,接口控制是否显示。

2.1 实现方式一:

我发现河狸家就是这个方式,为什么先以河狸家来举例呢?因为朋友说它太炫酷了!于是我就从它开始分析了。

我已经用越狱手机查看了河狸家APP的沙盒,并没有发现本地存储有皮肤文件。

于是我开始用Charles进行抓包,我在这个接口发现了疑似皮肤文件的配置信息。

如图:

于是我从img前缀的域名的中发下了请求到的皮肤文件,如图,这正是tabbar的背景图片。

值得称赞的是河狸家的png图片经过了webp压缩,这也是目前APP端主流的一个图片格式。

所以河狸家的方案是接口返回了皮肤的配置信息,配置信息中存有图片的地址信息,然后通过图片缓存框架去拿到图片的。

这种情况我认为一定做一下处理,让所有图片都缓存完毕后,再显示,不然可能会出现图片一个个闪现出来的情况,甚至于在网络不好的情况下,某个图片显示不出来的情况。这个情况我再另一个APP上见过,具体哪个我给忘记了(测试了好多APP,实在记不清了。。。)

2.2 实现方式二:

这里我以微店买家版进行一个举例,如图这是微店买家版圣诞节皮肤。

我同样是在安装APP后先看沙盒里是否有皮肤文件,同样并没有发现。下面直接去抓接口,我在assets的域名上发现了可疑的zip文件包。

如图:

解压这个zip文件后,发现了tabbar的图片资源。我同样在程序的沙盒里面发现了同样的文件。

如图:

图片资源拿到了,那么它们是如何替换的呢?我就以微店买家版进行举例来看一下。

我拿到微店买家版ipa脱壳后,我分别使用 Hopper Disassembler 和 class-dump 对主程序进行分析。最后发现如下信息:

从中可以看出它是使用的Category和KVO去实现了替换皮肤的过程。给UIButton等系统类添加一个Category,添加了设置皮肤的方法,通过KVO去实现了触发控制。
另外这里建议皮肤下载完成之后可以去立即触发换肤,我在测试百度糯米APP的时候发现它是第二次启动的时候才去替换,可能因为它是高频APP吧。

2.3 实现方式三:

这种方式我测试的几个APP中没有发现,听朋友说某知名APP曾经就采用过这个方式。这种方式是在发版前将皮肤文件存储到包内,通过后台接口控制去显示。这种情况的优点是便于控制,故障率小。缺点是包的体积过大,并且严重依赖于苹果爸爸的审核。

三、我的实现方式:

最近我也做了皮肤相关的功能,下面我说一下我的实现思路。
先上图,看一下我的APP控制逻辑。

我的实现思路类似微店的实现方式。但是我并没有使用KVO而是使用了通知注册的方式。
APP启动后直接加载对应的皮肤文件,同时另一个线程去请求后台皮肤接口,接口返回了一个zip包的链接,下载zip包,解压后,解析里面的config.json文件,然后我使用通知的方式去触发换肤。具体的思路逻辑相信流程图上已经画的很清楚了。
控制皮肤是否显示的逻辑完全由后台控制,后台返回skinSign为空则关闭皮肤。

下面看一下我的config.json文件的格式。

{"home_navi": {"colors": {"color_background": "#ffffff"},"images": {"image_logo": "home_topLogo"}},"home_tabbar": {"colors": {"color_background": "#F9F9F9","color_button_normal": "#999999","color_button_selected": "#444444"},"images": {"image_one_button_normal": "tab按钮1图片","image_one_button_selected": "tab按钮1选中图片","image_two_button_normal": "tab按钮2图片","image_two_button_selected": "tab按钮2选中图片","image_three_button_normal": "tab按钮2图片","image_three_button_selected": "tab按钮2选中图片"},"values": {"value_one_button": "tab按钮1","value_two_button": "tab按钮2","value_three_button": "tab按钮3"}},"loading": {"resources": {"resource_refreshImage" : "refresh.gif"} }
}

配置文件中,分为首页导航(home_navi)、首页tabbar(home_tabbar)、加载loading(loading)三个业务模块。在每个业务模块下都可以有4个功能模块分别是颜色(colors)、图片(images)、值(values)、资源(resources),这4个模块根据自己的需要进行添加。colors控制的是颜色,这里我以16进制值为准。images控制的是图片,最普通的png文件。values控制的是值。resources控制的是资源文件,例如json、gif等文件。

我创建了一个UIView的Category,在这个Category中我加了一个方法,如下:

- (void)configSkinMapModule:(NSString *)module skinMap:(NSDictionary *)skinMap;

假设我需要给导航栏添加换肤的功能,我只需要加上如下代码:

 [_tabbarButton configSkinMapModule:kSkin_MODULE_HOME_TABBAR skinMap:@{kSkinMapKey_button_image : @"image_one_button_normal",kSkinMapKey_button_selectedImage : @"image_one_button_selected",kSkinMapKey_button_titleColor : @"color_button_normal",kSkinMapKey_button_titleSelectedColor : @"color_button_selected",kSkinMapKey_button_title : @"value_one_button"}];

我会创建一个SkinConstants文件去定义一下,替换的方式标识。

static NSString * const kSkinMapKey_button_image = @"kSkinMapKey_button_image";
static NSString * const kSkinMapKey_button_highlightedImage = @"kSkinMapKey_button_highlightedImage";
static NSString * const kSkinMapKey_button_selectedImage = @"kSkinMapKey_button_selectedImage";
static NSString * const kSkinMapKey_button_disabledImage = @"kSkinMapKey_button_disabledImage";
static NSString * const kSkinMapKey_button_titleColor = @"kSkinMapKey_button_titleColor";
static NSString * const kSkinMapKey_button_titleHighlightedColor = @"kSkinMapKey_button_titleHighlightedColor";
static NSString * const kSkinMapKey_button_titleSelectedColor = @"kSkinMapKey_button_titleSelectedColor";
static NSString * const kSkinMapKey_button_titleDisabledColor = @"kSkinMapKey_button_titleDisabledColor";
static NSString * const kSkinMapKey_button_title = @"kSkinMapKey_button_title";static NSString * const kSkinMapKey_label_text = @"kSkinMapKey_label_text";
static NSString * const kSkinMapKey_label_textColor = @"kSkinMapKey_label_textColor";
static NSString * const kSkinMapKey_label_backgroundColor = @"kSkinMapKey_label_backgroundColor";static NSString * const kSkinMapKey_imageView_image = @"kSkinMapKey_imageView_image";
static NSString * const kSkinMapKey_imageView_gif = @"kSkinMapKey_imageView_gif";
static NSString * const kSkinMapKey_imageView_backgroundColor = @"kSkinMapKey_imageView_backgroundColor";

  相信从名字你们就能看出来,每一个定义都是UIKit里面的一个方法。

  然后我说一下刚才那个Category中加的方法,其中module对应的正是config.json中的业务模块,例如home_navi。skinMap中的key是替换的方式标识正是SkinConstants中的定义,value则是config.json中的对应的模块的key值。
也就是上面加的方法的意思是给这个home_navi业务模块中的某一个button增加了修改普通模式图片(kSkinMapKey_button_image)、修改选中模式图片(kSkinMapKey_button_selectedImage)、普通模式文字颜色(kSkinMapKey_button_titleColor)、修改选中模式图片(kSkinMapKey_button_selectedImage)、修改文字值(kSkinMapKey_button_title)的功能。

我们在通知触发方法中使用如下代码去执行替换过程

- (void)changeSkin
{NSDictionary *map = self.skinMap;if ([self isKindOfClass:[UIButton class]]) {UIButton *obj = (UIButton *)self;if (map[kSkinMapKey_button_image]) {[obj setImage:SkinImage(map[kSkinMapKey_button_image]) forState:UIControlStateNormal];}if (map[kSkinMapKey_button_highlightedImage]) {[obj setImage:SkinImage(map[kSkinMapKey_button_highlightedImage]) forState:UIControlStateHighlighted];}if (map[kSkinMapKey_button_selectedImage]) {[obj setImage:SkinImage(map[kSkinMapKey_button_selectedImage]) forState:UIControlStateSelected];}if (map[kSkinMapKey_button_disabledImage]) {[obj setImage:SkinImage(map[kSkinMapKey_button_disabledImage]) forState:UIControlStateDisabled];}if (map[kSkinMapKey_button_titleColor]) {[obj setTitleColor:SkinColor(map[kSkinMapKey_button_titleColor]) forState:UIControlStateNormal];}...以下省略...
}

同时我本地会存有一个localConfig.json用于管理本地的需要替换皮肤的模块,内容和config.json一模一样。只是他取的都是本地默认的皮肤资源配置。
SkinImage是处理images模块的,这个宏定义是pngResourceForSign:方法的宏,用于去处理该加载哪个图片文件。
关于colors、resources等其他模块我就不一一介绍了,都是大同小异。

- (UIImage *)pngResourceForSign:(NSString *)sign;
{NSArray *array = [sign componentsSeparatedByString:@"."];NSString *module = array.firstObject;NSString *key = array.lastObject;NSDictionary *moduleDic = self.configData[module];NSDictionary *imageDic = moduleDic[@"images"];NSString *value = imageDic[key];if (!self.path.length) {return [UIImage imageNamed:value];}NSString *filePath = [self.path stringByAppendingFormat:@"/%@",value];UIImage *image = [UIImage imageWithContentsOfFile:filePath];return image;
}

上面的例子就是_tabbarButton执行configSkinMapModule:skinMap:方法注册了一个通知,判断后台是否启用换肤,启动换肤则加载config.json文件,没有则加载localConfig.json本地默认皮肤。
以上就是我实现换肤方式的一个思路。

四、总结:

以上各种实现方式都各有各的好处,我的实现方式也有需要优化的地方,例如可以在后台接口上加入时间控制,可以实现提前的缓存方案,而不必每次都是在用户眼皮底下换。如果你有更好的实现方案欢迎一起交流。

参考资料:
1.github·ThemeManager
2.github·SwiftTheme
3.iOS换肤方案
4.github·EasyTheme
5.「节日换肤」通用技术方案__iOS端实现

转载于:https://www.cnblogs.com/weicyNo-1/p/8182339.html

iOS客户端节日换肤方案探究相关推荐

  1. iOS拓展---【转载】iOS客户端节日换肤方案探究

    [转载]iOS客户端节日换肤方案探究 一.前言: Tip: 本来这篇文章在圣诞节就已经准备好了,但是由于种种原因一直没有写完,今天将它写出来,也算是2018年的第一篇文章了.你好,2018! 过去圣诞 ...

  2. iOS客户端节日换肤的思考与实现

    最近单位的APP来了新的需求,市场说他们要在圣诞节把APP里的图标都换了,还要换背景图片,还要给部分view添加一个遮盖.对,就是换肤.以前没有搞过换肤,所以考虑了一天,感觉大概就是这么个思路,感觉有 ...

  3. Android 主题切换/换肤方案 研究(四) - qq和qq空间

    4. qq和qq空间 (独立app) 分析时用的是: 1. 夜神android模拟器(因为用android studio自带的模拟器运行x86架构的镜像提示不能安装qq空间,安装arm架构的镜像运行又 ...

  4. 最成熟的前端换肤方案(主题切换)

    前言 在网上找了很多的换肤方案,其中我认为写的最好的也是有demo 的无疑是这篇,但是同时也发现了一些问题,就是太多方案不知道选哪个,而且没有做持久化处理,并且没有把图片切换的代码放在一起.我这边的需 ...

  5. 换肤方案,换肤策略,App插件式换肤实现方案

    UI换皮肤或白天黑夜模式,从产品上来看,是两种不同产品设计模式:白天黑夜模式只有两种模式:而换皮肤可以有多套,可以进行商业化,并盈利. 换肤的本质就是去替换资源文件.我们知道,Android应用程序由 ...

  6. Android可更换布局的换肤方案

    换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...

  7. Android App节日换肤

    Android App节日换肤 Android App节日换肤 1原理 2使用方式 1在XML中给需要换肤的控件添加tag属性 2在Activity中使用 3还有疑问吧 3示例图 比如支付宝,饿了么, ...

  8. Android 应用换肤方案的总结

    虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换肤方案的可能 ...

  9. 对 Android 应用换肤方案的总结

    作者:me 虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换 ...

最新文章

  1. Cache系列:spring-cache简单三步快速应用ehcache3.x-jcache缓存(spring4.x)
  2. 5月16日 | 硬核突破,应用革新!阿里云数据库线下活动-北京站开启
  3. 中国行业趋势报告——2022年度特别报告
  4. 实验四 linux进程控制实验报告,Linux系统进程控制操作系统实验报告4
  5. springboot三层架构_几张图让你快速了解数据中台技术架构
  6. Anaconda 安装 OpenCV 遇到的问题
  7. 《鸟哥的Linux私房菜》读书笔记
  8. 游戏开发及游戏(2D/3D,Cocos2d Unity)小Demo(进行中)- Android
  9. vue 实现图片预览放大以及缩小
  10. 英语数字转换器(POJ NO.1123)
  11. 7-10 抢楼层 (20分) ---注意歧义啊!
  12. 机器学习笔记——乳腺癌鸢尾花分类问题详解(没有直接调包)
  13. 是谁在觊觎娱乐圈站点?揭秘神秘黑客组织-黑界
  14. Deep learning-based method coupled with small sample learning for solving partial differential equat
  15. 关于PEND SV的引入思考
  16. 台式计算机图形设置,如何打开计算机图形设置以提高游戏质量?
  17. JZJZJZ---数组中出现次数超过一半的数字
  18. 《基础会计学》期末模拟试题及答案
  19. [无线连接手机]-通过局域网无线连接调试手机设备
  20. 芯片制造和芯片技术研发同时突破,中国芯片开创新道路

热门文章

  1. weblogic的java代码日志_java – 重新启动WebLogic之前不生成日志文件
  2. 最新最全互联网大公司年终奖曝光:来看看年后跳槽哪一家吧!
  3. ICLOUD储存空间要升级吗_iCloud关闭好还是不好?免费储存空间用完了怎么备份手机?...
  4. springMVC_day01_概念_入门_@RequestMapping注解_参数封装与绑定_编码过滤器
  5. 【众莱思学习中心】最近很火的全脑教育是什么?
  6. css 强制不换行,保持在一行显示
  7. ​李宏毅机器学习——对抗生成网络(GAN)
  8. 电脑重要文件如何自动备份?
  9. PPT布尔运算之创意拆分字
  10. 华为gt3和华为gt3pro手表区别 华为gt3和gt3pro哪个值得入手