概述

目前市场上很多 App 都有主题变更、皮肤切换的功能。随着项目代码量的不断增长,业务不断完善,功能性代码逐渐趋于模块化,尤其是在多人协作开发同一个项目时,模块解耦尤为重要,同时,公共基础库的功能性代码使用越简单越好。

详细

一、前言

目前市场上很多 App 都有主题变更、皮肤切换的功能。随着项目代码量的不断增长,业务不断完善,功能性代码逐渐趋于模块化,尤其是在多人协作开发同一个项目时,模块解耦尤为重要,同时,公共基础库的功能性代码使用越简单越好。

前段时间在维护旧项目时,收到 App 主题变更、皮肤切换的需求,其包括 App 中各种图标、色值、文字、字体等都包括在内,都需实现主题化。主要用于:

  1. 活动主题展示:比较典型的是类似京东618、天猫淘宝购物节主题变更。

  2. 用户夜间模式:类似阅读相关 App 的夜间模式,如:简书等。

  3. 用户主题变更:用户可通过本地或者远程下载喜欢的主题,如:网易云音乐、QQ 音乐等 App 主题变更。

由于老项目代码比较混乱,功能模块耦合严重以及开发时间等综合因素,在实现 App 主题变更、皮肤切换的功能的同时,想要在尽量不修改旧代码的基础上增加新的功能是比较麻烦的。

由于没有合适的第三方库,于是自己手撸了一个库 SakuraKit,并开源,希望能帮到需要的朋友。

下面我们开始介绍 SakuraKit 及快速入门。

二、SakuraKit

SakuraKit,是一个轻量级的、专门用于 App 主题变更、皮肤切换的开源库(灵感源自 SwiftTheme、DKNightVersion等),采用函数式 + 链式的编码方式,简单实用、方便理解、利于维护。

三、快速入门

效果

在体验前,我们先来看看效果图:

体验

下面以 UIButton 为例,介绍如何使用 SakuraKit 进行主题化:

UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];

button.sakura.backgroundColor(@"Home.buttonBackgroundColor").titleColor(@"Home.buttonTitleColor", UIControlStateNormal);

上述代码是给一个 button 的背景色(backgroundColor)以及标题颜色(titleColor)进行主题化。其中 Home.buttonBackgroundColor 与Home.buttonTitleColor 属配置文件中的 KeyPath,配置文件的功能有点类似语言本地化文件(Localizable.strings)。后文会重点介绍如何设置配置文件。

到此为止,我们已经实现了 button 按钮主题化功能,如果你想切换主题,可以调用如下 API:

+ (BOOL)shiftSakuraWithName:(TXSakuraName *)name type:(TXSakuraType)type;

其中 name 参数代表主题的名称,type 参数代表主题类型(目前有两种:沙盒和本地)。

现在我们再具体的介绍一下如何使用 SakuraKit

四、配置文件

做过 App 语言本地化的童鞋,应该比较熟悉 Localizable.strings 文件配置,同理,我们在使用 SakuraKit 对 App 进行主题化时,也需要进行类似的配置。目前支持 .json 和 .plist 两种文件格式。

下面我们以 .json 文件格式做示例:

{
"Home":{
"buttonBackgroundColor":"#BB503D",
"buttonTitleColor":"#4AF2A1"
}}

在上述体验代码中,我们看到这样的字符串:Home.buttonBackgroundColor 和 Home.buttonTitleColor,这其实就是配置文件中字典的 KeyPath,通过 KeyPath 可以取得不同主题下的值,如:色值、图片名称、文字、字体大小等等。

注意事项:

  1. 每个主题都有自己配置文件,包括本地和沙盒主题。(本地主题名叫 default)。

  2. 主题名称与配置文件名称一致,如:某个主题名叫 fish,那么该主题相应的配置文件就应命名为fish.json。(建议遵守该约定)

  3. 不同本地主题的切图命名要做区分,不同远程主题的切图命名应一致。

五、本地主题

本地主题,即用户无需下载的主题,在 App Bundle 中。除了 App 本身自带的默认主题外,SakuraKit 还能够为 App 新增多种本地主题。

配置步骤如下:

步骤一

新建 .json 配置文件,比如新建一个名叫 typewriter 的主题,因此配置文件命名为 typewriter.json。

步骤二

配置一套切图,并且命名与已有的主题要做区分。

步骤三

完成上述步骤后,在 AppDelegate 中 -application:application didFinishLaunchingWithOptions:launchOptions API 注册所有本地主题:

// 注意:本地默认主题无需注册[TXSakuraManager registerLocalSakuraWithNames:@[@"typewriter"]];

步骤四

调用切换主题 API 即可切换至该指定主题:

[TXSakuraManager shiftSakuraWithName:@"typewriter" type:TXSakuraTypeMainBundle];

六、远程主题

远程主题(资源压缩包.zip),即用户通过网络下载的主题,后台可动态配置。同本地主题一致,分为两部分:配置文件 + 切图。当配置文件和切图都弄好后,将文件夹打包成zip文件,传给后台即可。主题数据格式如下(仅供参考):

{
"name": "嘻多猴",
"sakuraName": "monkey",
"url": "http:\\image.tingxins.cn\sakura\monkey.zip"}

sakuraName 是切换主题时用的名称,而 url 是该主题的下载地址。(注:如果 sakuraName 字段传空,那么主题的名称将默认为下载的压缩包名称)

当远程主题下载完毕后,可以这样切换主题:

[TXSakuraManager shiftSakuraWithName:sakuraName type:TXSakuraTypeSandBox];

值得一提的是,SakuraKit 提供了一些主题下载的简单接口,支持多种主题同时下载等操作,并且支持 Block 和 Delegate 两种方式的回调,同时用户还可自定义下载操作。

下面我们来依次介绍一下主题下载。

Block 方式

我们直接来介绍 API :

[[TXSakuraManager manager] tx_sakuraDownloadWithInfos:sakuraModel downloadProgressHandler:^(int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {    // 下载进度回调} downloadErrorHandler:^(NSError * _Nullable error) {    // 下载过程出现错误回调} unzipProgressHandler:^(unsigned long long loaded, unsigned long long total) {    // 主题下载完成后,解压进度回调} completedHandler:^(id<TXSakuraDownloadProtocol> _Nullable infos, NSURL * _Nullable location) {    // 主题包解压完毕回调} ];

其中 sakuraModel 模型数据遵守了 TXSakuraDownloadProtocol 协议,具体使用详见 SakuraDemo_OC,在 DownloadSakuraController 控制器演示了该操作。

Delegate 方式

步骤一

直接调用 API 实现主题下载:

[[TXSakuraManager manager] tx_sakuraDownloadWithInfos:sakuraModel delegate:self];
步骤二

如果针对步骤一的下载操作需要回调,那么可以选择性的再实现以下方法:

// 重复点击下载某一主题,如果该主题已经处于下载中或者本地存在时将会回调,其中 status 标识该 downloadTask 状态。- (void)sakuraManagerDownload:(TXSakuraManager *)manager
downloadTask:(NSURLSessionDownloadTask *)downloadTask
status:(TXSakuraDownloadTaskStatus)status;// 主题下载完毕时回调,其中 infos 包括主题名称,可通过该参数直接切换至该主题- (void)sakuraManagerDownload:(TXSakuraManager *)manager
downloadTask:(NSURLSessionDownloadTask *)downloadTask
sakuraInfos:(id<TXSakuraDownloadProtocol>)infos
didFinishDownloadingToURL:(NSURL *)location;// 主题下载进度- (void)sakuraManagerDownload:(TXSakuraManager *)manager
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;/** Reserved for future use */- (void)sakuraManagerDownload:(TXSakuraManager *)manager
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;// 下载操作出现错误时回调- (void)sakuraManagerDownload:(TXSakuraManager *)manager
sessionTask:(NSURLSessionTask *)downloadTask
didCompleteWithError:(nullable NSError *)error;// 主题下载包解压进度回调- (void)sakuraManagerDownload:(TXSakuraManager *)manager
downloadTask:(NSURLSessionDownloadTask *)downloadTask
progressEvent:(unsigned long long)loaded
total:(unsigned long long)total;

具体使用详见 SakuraDemo_OC,在 AppDelegate 中演示了该操作。

自定义下载操作

除了上述自带的下载操作外,SakuraKit 还提供了自定义下载操作相关的 API :

// sakuraModel 模型数据遵守了 TXSakuraDownloadProtocol 协议,location 即自定义下载下来的主题包地址。[[TXSakuraManager manager] tx_generatePathWithInfos:sakuraModel downloadFileLocalURL:location successHandler:^(NSString *toFilePath, NSString *sakuraPath, TXSakuraName *sakuraName) {                  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{      BOOL isSuccess = [SSZipArchive unzipFileAtPath:toFilePath toDestination:sakuraPath delegate:self];      // 注意:自定义下载操作,必须进行 Sakura 路径格式化!Required!
[TXSakuraManager formatSakuraPath:sakuraPath cleanCachePath:toFilePath];
dispatch_sync(dispatch_get_main_queue(), ^{ if (isSuccess) {
[TXSakuraManager shiftSakuraWithName:sakuraName type:TXSakuraTypeSandBox];
}
});
});
} errorHandler:^(NSError * _Nullable error) { NSLog(@"errorDescription:%@",error);
}];

七、FAQ

1.为何每个主题都有自己配置文件?

答: 由于每个主题,除了切图的命名是是一致的外,不同的主题背景色、字体大小可能不一样,因此,每个主题都要有自己的配置文件,除非只对切图进行本地化。

2.为何主题名称与配置文件名称一致?

答: 这只是一个约定,SakuraKit 会通过主题名称找到该主题在本地或者在沙盒中的路径,使得主题名称与配置文件名称一致,可以减少不必要的工作量。

3.本地与沙盒主题有什么区别?

答: 在本地主题称为 mainBundle 主题,远程主题称为 Sandbox 主题。

Demo 素材来源:网易云音乐等第三方 App,如有不妥之处,请及时联系并予以删除,谢谢。

八、项目文件截图

欢迎关注微信公众号

注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

iOS主题/皮肤之SakuraKit的更多相关文章

  1. iOS 主题/皮肤之 SakuraKit

    前言 目前市场上很多 App 都有主题变更.皮肤切换的功能.随着项目代码量的不断增长,业务不断完善,功能性代码逐渐趋于模块化,尤其是在多人协作开发同一个项目时,模块解耦尤为重要,同时,公共基础库的功能 ...

  2. iOS开发之主题皮肤

    iOS开发之主题皮肤 分类: [iOS]  最近在开发一款[公交应用],里面有个模块涉及到主题设置,这篇文章主要谈一下个人的做法. 大概的步骤如下: (1):整个应用依赖于一个主题管理器,主题管理器根 ...

  3. 【Android Studio安装部署系列】八、Android Studio主题皮肤更换

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 Android Studio具有自己的主题皮肤,但是如果想要更换自己喜欢的主题皮肤,可以参考下面的步骤. 注意,更换主题皮肤,之前的 ...

  4. jQuery实现无刷新切换主题皮肤功能

    主题皮肤切换功能在很多网站和系统中应用,用户可以根据此功能设置自己喜欢的主题颜色风格,增强了用户体验.本文将围绕如何使用jQuery实现点击无刷新切换主题皮肤功能. 查看演示DEMO:https:// ...

  5. 推荐一套WPF主题皮肤

    在CodePlex上发现了一套WPF的主题皮肤,直接应用于系统自带的控件,一共有四种配色方案,做得也还比较精致.感兴趣的朋友可以使用一下.点击下载      另外一套我比较喜欢的皮肤这里也推荐一下吧: ...

  6. 微信小程序 主题皮肤切换(switch开关)

    示例效果: 功能点分析: 1.点击switch开关,切换主题皮肤(包括标题栏.底部tabBar):2.把皮肤设置保存到全局变量,在访问其它页面时也能有效果3.把设置保存到本地,退出应用再进来时,依然加 ...

  7. Android 实现切换主题皮肤功能(类似于众多app中的 夜间模式,主题包等)

    首先来个最简单的一键切换主题功能,就做个白天和晚上的主题好了. 先看我们的styles文件: <resources> <!-- Base application theme. --& ...

  8. Sencha Touch 2.3 自定义主题皮肤,颜色

    写博客园越来越懒了,只写重点部分,不明白的可以Q我. 1.当你通过Cmd生成项目之后,App\resources\sass就是我们的样式源文件 2.想自定义自己的样式分为两种方式 扩展:这个是基于st ...

  9. 博客美化——Silence主题皮肤

    介绍   一款专注阅读的博客园主题,主要面向于经常混迹 博客园 的朋友.其追求大道至简的终极真理,界面追求简洁.运行追求高效.部署追求简单. 博客皮肤源码地址 预览地址 如何部署.使用皮肤 Silen ...

随机推荐

  1. Java高级架构师(一)第40节:更多模块的基本功能和配置

  2. hdu 4119 Isabella's Message 模拟题

    Isabella's Message Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.p ...

  3. SQl CASE 语句的嵌套使用方式

    case具有两种格式.简单case函数和case搜索函数.  1.简单case函数 case sex when ’1’ then ’男’ when ’2’ then ’女’else ’其他’ end ...

  4. HDU 1171 Big Event in HDU (多重背包变形)

    Big Event in HDU Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  5. MOSFET enhances voltage regulator's overcurrent protection

    The classic LM317 adjustable-output linear voltage regulator offers a relatively high, if package-de ...

  6. 采集音频和摄像头视频并实时H264编码及AAC编码[转]

    0. 前言 我在前两篇文章中写了DirectShow捕获音视频然后生成avi,再进行264编码的方法.那种方法有一些局限性,不适合实时性质的应用,如:视频会议.视频聊天.视频监控等.本文所使用的技术, ...

  7. Appium+python自动化17-启动iOS模拟器APP源码案例

    前言 上一篇已经可以启动iOS模拟器上的safari浏览器了,启动app比启动浏览器要复杂一点,本篇以github上的源码为案例详细介绍如何启动iOS模拟器的app 一.clone源码 1.githu ...

  8. Mysql -- 统计类用法

    累加: update push_online a,(select msg_key, push_countfrom push_online)b set a.push_count=b.push_count ...

  9. Python学习(四)数据结构 —— int float

    Python 数字类型 int float 数字常量 int: 一般的整数, long:   长整型,2.x版本需在数字后加 “L” 或 “l” ,表示长整型 如 100000000L: python ...

  10. 杭电OJ——1032 The 3n + 1 problem

    The 3n + 1 problem Problem Description Problems in Computer Science are often classified as belongin ...