iOS用戶端節日換膚方案探究

來源:互聯網
上載者:User

標籤:str   nss   資源檔   直接   body   component   back   源檔案   故障   

轉自: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端實現

iOS用戶端節日換膚方案探究

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.