iOS 主題/皮膚之 SakuraKit

sakura-kit-logo

前言

目前市場上很多 App 都有主題變更、皮膚切換的功能。隨著項目代碼量的不斷增長,業(yè)務不斷完善,功能性代碼逐漸趨于模塊化,尤其是在多人協(xié)作開發(fā)同一個項目時,模塊解耦尤為重要,同時,公共基礎庫的功能性代碼使用越簡單越好。

前段時間在維護舊項目時,收到 App 主題變更、皮膚切換的需求,其包括 App 中各種圖標、色值、文字、字體等都包括在內,都需實現(xiàn)主題化。主要用于:

  1. 活動主題展示:比較典型的是類似京東618、天貓?zhí)詫氋徫锕?jié)主題變更。
  2. 用戶夜間模式:類似閱讀相關 App 的夜間模式,如:簡書等。
  3. 用戶主題變更:用戶可通過本地或者遠程下載喜歡的主題,如:網(wǎng)易云音樂、QQ 音樂等 App 主題變更。

由于老項目代碼比較混亂,功能模塊耦合嚴重以及開發(fā)時間等綜合因素,在實現(xiàn) App 主題變更、皮膚切換的功能的同時,想要在盡量不修改舊代碼的基礎上增加新的功能是比較麻煩的。

由于沒有合適的第三方庫,于是自己手擼了一個庫 SakuraKit,并開源,希望能幫到需要的朋友。

下面我們開始介紹 SakuraKit 及快速入門。

SakuraKit

SakuraKit,是一個輕量級的、專門用于 App 主題變更、皮膚切換的開源庫(靈感源自 SwiftTheme、DKNightVersion等),采用函數(shù)式 + 鏈式的編碼方式,簡單實用、方便理解、利于維護。

快速入門

效果

在體驗前,我們先來看看效果圖:

sakura-kit-demo

體驗

下面以 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.buttonBackgroundColorHome.buttonTitleColor 屬配置文件中的 KeyPath,配置文件的功能有點類似語言本地化文件(Localizable.strings)。后文會重點介紹如何設置配置文件。

到此為止,我們已經(jīng)實現(xiàn)了 button 按鈕主題化功能,如果你想切換主題,可以調用如下 API:


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

其中 name 參數(shù)代表主題的名稱,type 參數(shù)代表主題類型(目前有兩種:沙盒本地)。

現(xiàn)在我們再具體的介紹一下如何使用 SakuraKit

配置文件

做過 App 語言本地化的童鞋,應該比較熟悉 Localizable.strings 文件配置,同理,我們在使用 SakuraKit 對 App 進行主題化時,也需要進行類似的配置。目前支持 .json.plist 兩種文件格式。

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

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

在上述體驗代碼中,我們看到這樣的字符串:Home.buttonBackgroundColorHome.buttonTitleColor,這其實就是配置文件中字典的 KeyPath,通過 KeyPath 可以取得不同主題下的值,如:色值、圖片名稱、文字、字體大小等等。

注意事項:

  1. 每個主題都有自己配置文件,包括本地和沙盒主題。(本地主題名叫 default)。
  2. 主題名稱與配置文件名稱一致,如:某個主題名叫 fish,那么該主題相應的配置文件就應命名為fish.json。(建議遵守該約定
  3. 不同本地主題的切圖命名要做區(qū)分,不同遠程主題的切圖命名應一致。

本地主題

本地主題,即用戶無需下載的主題,在 App Bundle 中。除了 App 本身自帶的默認主題外,SakuraKit 還能夠為 App 新增多種本地主題。

配置步驟如下:

步驟一

新建 .json 配置文件,比如新建一個名叫 typewriter 的主題,因此配置文件命名為 typewriter.json。

步驟二

配置一套切圖,并且命名與已有的主題要做區(qū)分。

步驟三

完成上述步驟后,在 AppDelegate 中 -application:application didFinishLaunchingWithOptions:launchOptions API 注冊所有本地主題:


// 注意:本地默認主題無需注冊
[TXSakuraManager registerLocalSakuraWithNames:@[@"typewriter"]];

步驟四

調用切換主題 API 即可切換至該指定主題:


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

遠程主題

遠程主題(資源壓縮包.zip),即用戶通過網(wǎng)絡下載的主題,后臺可動態(tài)配置。同本地主題一致,分為兩部分:配置文件 + 切圖。當配置文件和切圖都弄好后,將文件夾打包成zip文件,傳給后臺即可。主題數(shù)據(jù)格式如下(僅供參考):


{
    "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) {
    // 下載過程出現(xiàn)錯誤回調
} unzipProgressHandler:^(unsigned long long loaded, unsigned long long total) {
    // 主題下載完成后,解壓進度回調
} completedHandler:^(id<TXSakuraDownloadProtocol> _Nullable infos, NSURL * _Nullable location) {
    // 主題包解壓完畢回調
} ];


其中 sakuraModel 模型數(shù)據(jù)遵守了 TXSakuraDownloadProtocol 協(xié)議,具體使用詳見 SakuraDemo_OC,在 DownloadSakuraController 控制器演示了該操作。

Delegate 方式

步驟一

直接調用 API 實現(xiàn)主題下載:


[[TXSakuraManager manager] tx_sakuraDownloadWithInfos:sakuraModel delegate:self];


步驟二

如果針對步驟一的下載操作需要回調,那么可以選擇性的再實現(xiàn)以下方法:


// 重復點擊下載某一主題,如果該主題已經(jīng)處于下載中或者本地存在時將會回調,其中 status 標識該 downloadTask 狀態(tài)。
- (void)sakuraManagerDownload:(TXSakuraManager *)manager
                 downloadTask:(NSURLSessionDownloadTask *)downloadTask
                       status:(TXSakuraDownloadTaskStatus)status;

// 主題下載完畢時回調,其中 infos 包括主題名稱,可通過該參數(shù)直接切換至該主題
- (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;

// 下載操作出現(xiàn)錯誤時回調
- (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 模型數(shù)據(jù)遵守了 TXSakuraDownloadProtocol 協(xié)議,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);
}];

FQA

1.為何每個主題都有自己配置文件?
答:由于每個主題,除了切圖的命名是是一致的外,不同的主題背景色、字體大小可能不一樣,因此,每個主題都要有自己的配置文件,除非只對切圖進行本地化。

2.為何主題名稱與配置文件名稱一致?
答:這只是一個約定,SakuraKit 會通過主題名稱找到該主題在本地或者在沙盒中的路徑,使得主題名稱與配置文件名稱一致,可以減少不必要的工作量。

3.本地與沙盒主題有什么區(qū)別?
答:在本地主題稱為 mainBundle 主題,遠程主題稱為 Sandbox 主題。

開源

關于 SakuraKit 具體使用,詳見 Demo。

GitHub 項目地址:https://github.com/tingxins/SakuraKit

有什么問題或者更好的建議,GitHub 上直接提 issue 或者 PR。感謝支持

Demo 素材來源:網(wǎng)易云音樂等第三方 App,如有不妥之處,請及時聯(lián)系并予以刪除,謝謝。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,634評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,167評論 25 708
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,278評論 6 342
  • 隨著主持人的上場,節(jié)目終于開始正式進入錄制。到底曾是電視臺的招牌節(jié)目,即使在受冷落的情況下,節(jié)目水準還是比一般電視...
    小wo閱讀 436評論 0 1
  • 啟示1: 了解你的極限。意志力供給是有限的,從同一賬戶提取意志力用于各種不同任務,包括各類瑣碎事情,等車等。 2:...
    西瓜弟的夏天閱讀 1,011評論 0 1

友情鏈接更多精彩內容