节日换肤通用技术方案__iOS端实现
一、问题的提出
不知道大家有没有发现, 元旦期间, 很多APP界面里的图标都换成了具有节日气氛的样式, 而在过了元旦节之后, 这些图标又悄无声息的变回了本来的面貌.
这些具有短暂生命周期、而又必须在固定时间节点上展示的节日皮肤, 究竟是如何实现的呢? 显然, 通过发布新版本可以实现, 但是对于iOS端的应用来说, 面对苹果APP Store不确定的审核时间, 开发人员往往需要提前1~2周完成并提交审核, 而且每到一个节日都要重新发布一个新版本, 难免略显被动.
- 热更新是一个不错的选择!
试想一下, 如果把需要更换皮肤主题的所有界面元素抽象出一个完整的业务模型(Model), 再由一个专门的Skin Manager去负责每次启动时的验证和异步加载, 那么以后所有的节日皮肤的发布, 我们都只需要后台改一个参数, 就能够使用户的APP即时的更新到最新的皮肤主题了.
当然, 如果你的应用是采用的React Native的话, 换肤就更简单了, 不过呢, 本文是对iOS原生应用提出一套普遍通用的解决方案, 仅仅是抛砖引玉, 如果你有更优化的方案, 欢迎留言提出, 大家共同探讨学习.
好的, 那我们接下来 试着玩一玩~
二、业务流程
做一件事之前, 要先理清思路, 做什么、怎么做, 既然方向已经确定下来了, 那么需要梳理一下头绪, 我整理出这么一个流程图:

三、开敲代码
其实流程图中的每一个方块就是我们需要的方法, 一一实现即可.
请求接口, 判断, 有三种情况, 删除操作用NSFilemanafer, 下载用AFnetworking, 解压缩用到SSZipArchive这都没什么好说的, 直接上代码就可以了.
这其中有一点值得讨论, 就是最后加载皮肤的方式, 我一开始设想的用KVO, 后来发现NotificationCenter会方便一些, 但是还是会很复杂, 如果要修改的皮肤很多, 涉及到的文件也很多, 难道每一个都要都要去注册一个通知中心和一个接收方法吗? 还是有点复杂, 乔帮主说做事要化繁为简, 于是闭上眼睛冥想了一阵, 突然想到了一个方法, 把imageNamed:和colorWith两个基本方法进行替换, 在manager里写两个对应的新方法, 替换掉需要换肤的图片或者文字颜色部分的系统方法, 新方法里面加一个判断, 如果资源文件路径存在, 则显示资源路径下的图片或者解析到的颜色值; 如果资源路径不存在, 则读取默认bundle下的资源. 这样一来, 只需要在原来庞大的工程文件中找到需要修改皮肤的地方, 把他们原来的方法替换就OK了, 节省了很多事, 而且减少暴露在manager外面的逻辑代码, 对于维护也很重要! 好的, 直接上代码:
下面的这个是单例里的两个方法, 专门用来替换需要换肤的组件的原先的方法, 看起来很简洁:
- (UIImage*)imageNamedAutoMatch:(NSString *)name
{
return [UIImage imageWithContentsOfFile:_currentPath];
}
- (UIColor*)colorAutoMatch:(NSString *)itemName
{
return [UIColor colorWithHexString:_colorArray[itemName]];
}
头文件是这样的, 常用路径写到宏里:
#define Name_Resource @"HolidaySkin"
#define DocumentPath [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:Name_Resource]
#define Bundle_Path [[NSBundle mainBundle]resourcePath]
#define kRequestData /*相应的入参*/ @interface DRSkinManager : NSObject @property (nonatomic, copy)NSString *skinStatus;
@property (nonatomic, copy)NSString *downloadUrl;
@property (nonatomic, copy)NSString *currentPath;
@property (nonatomic, strong)NSArray *colorArray; + (instancetype)shareManager; - (UIImage*)imageNamedAutoMatch:(NSString *)name;
- (UIColor*)colorAutoMatch:(NSString *)itemName;
- 流程图第一步的接口请求, 没什么好说的:
- (void)checkSkinStatue
{
[DRNetwork requestWithDictionary:kRequestData response:^(BOOL success, id result) {
if (success) {
NSString *skinStaus = result[@"skinStaus"];
NSString *downloadUrl = result[@"downloadUrl"];
[self judgeResourceStatus:skinStatus url:downloadUrl];
}
}
}];
}
- 比对状态值, 三种情况, 三个分支:
- (void)judgeResourceStatus:(NSString *)skinStaus url:(NSString *)downloadUrl
{
if (skinStaus.length == ) {
return;
} NSString *localSkinStatus = [[NSUserDefaults standardUserDefaults]valueForKey:Name_Resource]; if ([skinStaus isEqualToString:localSkinStatus]) {
self.skinStatus = skinStaus;
self.currentPath = [DocumentPath stringByAppendingPathComponent:skinStaus];
return;
} if (![skinStaus isEqualToString:localSkinStatus]) {
if (localSkinStatus.length != ) {
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:[DocumentPath stringByAppendingPathComponent:localSkinStatus] error:nil];
}
self.downloadUrl = downloadUrl;
[self downLoadAndUnzip];
}
}下载和解压
- (void)downLoadAndUnzip
{
NSString *zipPath = [DocumentPath stringByAppendingString:@"/dontcare.zip"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc]initWithRequest:_downloadUrl];
[operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:zipPath append:NO]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { //下载成功后: 解压缩
[SSZipArchive unzipFileAtPath:zipPath toDestination:[DocumentPath stringByAppendingPathComponent:_skinStatus] progressHandler:^(NSString *entry, unz_file_info zipInfo, long entryNumber, long total){ }completionHandler:^(NSString *path, BOOL succeeded, NSError *error){
//解压完成, 至此皮肤资源已经完整缓存到沙盒, 删除zip包, 保存skinStatus
NSFileManager *fileManager = NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:zipPath error:nil];
[[NSUserDefaults standardUserDefaults]setValue:_skinStatus forKey:Name_Resource];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
success(operation,error); NSLog(@"下载失败"); }]; [operation start];
}
另一种换肤模式:比如系统内部切换模式(白天,黑夜 或者自定义肤色 或者图片)
有一个好用的第三方 (给系统的控件添加分类)
https://github.com/Draveness/DKNightVersion
节日换肤通用技术方案__iOS端实现的更多相关文章
- iOS端实现节日换肤
本文是我在网上看到一篇不错的文章,因为之前没接触过,所以特意转过来,和大家一起分享下..以下正文: 一.问题的提出 不知道大家有没有发现, 元旦期间, 很多APP界面里的图标都换成了具有节日气氛的样式 ...
- 一文读懂HarmonyOS服务卡片怎么换肤
作者:zhenyu,华为软件开发工程师 关注HarmonyOS的小伙伴肯定对服务卡片已经很熟悉了.服务卡片(也简称为"卡片")是FA(FeatureAbility,元服务)的一种界 ...
- Android换肤技术总结
原文出处: http://blog.zhaiyifan.cn/2015/09/10/Android%E6%8D%A2%E8%82%A4%E6%8A%80%E6%9C%AF%E6%80%BB%E7%BB ...
- 全面解密QQ红包技术方案:架构、技术实现、移动端优化、创新玩法等
本文来自腾讯QQ技术团队工程师许灵锋.周海发的技术分享. 一.引言 自 2015 年春节以来,QQ 春节红包经历了企业红包(2015 年).刷一刷红包(2016 年)和 AR 红包(2017 年)几个 ...
- 如何写好B端产品的技术方案?
B端产品为企业提供协同办公的工具,帮助企业解决某类经营管理问题,核心价值在于为企业增加收入.降本提效.管控风险,企业级SaaS产品也是B端产品中的一类. B端产品有以下特点: 客户是一个群体:B端产 ...
- 利用CSS预处理技术实现项目换肤功能(less css + asp.net mvc4.0 bundle)
一.背景 在越来越重视用户体验的今天,换肤功能也慢慢被重视起来.一个web系统用户可以选择一个自己喜欢的系统主题,在用户眼里还是会多少加点分的.我们很开心的是easyui v1.3.4有自带defau ...
- .NET Web后台动态加载Css、JS 文件,换肤方案
后台动态加载文件代码: //假设css文件:TestCss.css #region 动态加载css文件 public void AddCss() { HtmlGenericControl _CssFi ...
- Android可更换布局的换肤方案
换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...
- vue+less换肤,主题切换方案
新的项目对于客户自定义要求很高,然后换肤是其中一个很小的模块,经过了一段时间的摸索,看了许多文章,找到了几种方案. https://www.cnblogs.com/leiting/p/11203383 ...
随机推荐
- C#之内存分配
在C#中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数等 ...
- 目标检测之rcnn---开启检测新高度优于dpm
http://www.cnblogs.com/louyihang-loves-baiyan/p/4839869.html http://www.cnblogs.com/louyihang-loves- ...
- (转)【多媒体封装格式详解】--- AAC ADTS格式分析
出自:http://blog.csdn.net/tx3344/article/details/7414543 http://www.it6655.com/2012/08/aac-adts-html ...
- (诊断)解决GitHub使用双因子身份认证“Two-Factor Athentication”后无法git push 代码的“fatal: Authentication failed for ...”错误
在GitHub上采取双因子身份认证后,在git push 的时候将会要求填写用户的用户名和密码,用户名就是用户在GitHub上申请的用户名,但是密码不是普通登录GitHub的密码. 一旦采取双因子身份 ...
- 全新升级的WiFi无线上网短信认证系统,适用于咖啡厅、足浴等公共场所,提高门门店营业收入
WiFi无线上网短信认证系统经历从1.0到1.88的升级,都是用户在使用过程中,提出宝贵的意见,一直修复至今,有着非常稳定的版本. 这个软件有什么作用?WiFi为什么要认证呢? 其实这个只是获取用户联 ...
- windows上開啟多個apache服務器
1.安裝apache(這裡我用的是集成環境) 比較php版本 5.6 與 7.2 比較mysql版本 拓展: 注意:對個不同的版本的mysql,命令行進入,需要指明端口號,如:mysql -uroo ...
- jquery带token访问接口ajax
1.在公共js里全局设置 //设置全局ajax $.ajaxSetup({ xhrFields: { withCredentials: true } }); 2.登录成功时,设置cookie (注: ...
- Mongodb学习笔记(1)--入门
文档 多个键及关联的值有序的放置在一起就是文档,如"greeting":"Hello World!" 特点 文档中键值对是有序的 除了字符串还可以是其他类型:& ...
- springboot+mybatis集成多数据源MySQL/Oracle/SqlServer
日常开发中可能时常会遇到一些这样的需求,业务数据库和第三方数据库,两个或多个数据库属于不同数据库厂商,这时候就需要通过配置来实现对数据库实现多源处理.大致说一下我的业务场景,框架本身是配置的sprin ...
- Python 程序员都会喜欢的 6 个库
在编程时,小挫折可能与大难题一样令人痛苦.没人希望在费劲心思之后,只是做到弹出消息窗口或是快速写入数据库.因此,程序员都会喜欢那些能够快速处理这些问题,同时长远来看也很健壮的解决方案. 下面这6个Py ...