iOS 10.3 如何動態(tài)更換App圖標

原文鏈接:http://www.lxweimin.com/p/dd5144f8f021

動態(tài)更換App圖標這件事,在用戶里總是存在需求的:有些用戶喜歡“美化”自己的手機。至于用戶們喜歡美化到什么程度,這得看個人需求。有的用戶想定制個性的App圖標,那么各大iPhone論壇里都有方法可以不越獄更改App圖標;有的用戶想讓App圖標“動”起來(如系統(tǒng)應(yīng)用時鐘),那么不越個獄還真不好辦。

不過今天我們想談?wù)勌O果官方對于動態(tài)更換App圖標的支持。

本系列文章

iOS動態(tài)更換App圖標(一):基礎(chǔ)使用:http://daiyi.pro/2017/05/01/ChangeYourAppIcons1/
iOS動態(tài)更換App圖標(二):無彈框更換App圖標:http://daiyi.pro/2017/05/01/ChangeYourAppIcons2/
iOS動態(tài)更換App圖標(三):動態(tài)下載App圖標進行更換
Demo演示

DynamicAppIconDemo1
Demo地址:https://github.com/maybeisyi/ChangeAppIconDemo

本篇文章對應(yīng)工程為:DynamicAppIcon(一)

Demo中可以看到,在不重新安裝App的情況下,可以實現(xiàn)更新App的圖標。但是會彈出一個提示,告知用戶當前圖標已更換,當然下一篇文章將會突破這個“限制”。

該功能應(yīng)用的場景

1、白天/夜間模式切換,在切換App主色調(diào)同時切換App圖標。

2、各類皮膚主題(淘寶就可換膚),附帶App圖標一塊更換。

3、利用App圖標表達某種特定功能,如Demo中的,提示當前天氣。

4、圖標促銷提示,如淘寶京東特定節(jié)日:11.11、6.18,提前更換App圖標。

當然該功能(API)當前只支持iOS10.3以上的系統(tǒng),所以只能當做一項附加功能來進行使用。下面將詳細講解下如何使用代碼來實現(xiàn)此功能。

API與文檔

API方法

@interface UIApplication (UIAlternateApplicationIcons)
// 如果為NO,表示當前進程不支持替換圖標
@property (readonly, nonatomic) BOOL supportsAlternateIcons NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));

// 傳入nil代表使用主圖標. 完成后的操作將會在任意的后臺隊列中異步執(zhí)行; 如果需要更改UI,請確保在主隊列中執(zhí)行.
- (void)setAlternateIconName:(nullable NSString *)alternateIconName completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));

// 如果alternateIconName為nil,則代表當前使用的是主圖標.
@property (nullable, readonly, nonatomic) NSString *alternateIconName NS_EXTENSION_UNAVAILABLE("Extensions may not have alternate icons") API_AVAILABLE(ios(10.3), tvos(10.2));
@end

總共3個方法,簡潔明了,不過但看這3個API,我們并不清楚alternateIconName是如何與app圖標掛鉤的,所以我們需要進一步翻看文檔。

文檔

shift+command+0打開文檔,依次查看3個API,翻譯如下:

supportsAlternateIcons

supportsAlternateIcons Document
(翻譯)只有系統(tǒng)允許改變你的app圖標時該值才為YES。你需要在Info.plist文件中的CFBundleIcons這個鍵內(nèi)聲明可更換的app圖標。

?

alternateIconName

alternateIconName Document
(翻譯)當系統(tǒng)展示的是你更換后的app圖標時,該值即為圖標名字(Info.plist中定義的圖標名字)。如果展示的是主圖標時,這個值為nil。

?

setAlternateIconName:completionHandler:

setAlternateIconName Document
(翻譯)alertnateIconName參數(shù):該參數(shù)為需要更換的app圖標名字,是在你的Info.plist中的CFBundleAlertnateIcons鍵里定義的。如果你想顯示的是用CFBundlePrimaryIcon鍵所定義的主圖標的話,就傳入nil。CFBundleAlertnateIcons與CFBundlePrimaryIcon鍵都是在CFBundleIcons里面定義的。

(翻譯)completionHandler參數(shù):該參數(shù)用來處理(更換)結(jié)果。當系統(tǒng)嘗試更改app的圖標后,會將結(jié)果數(shù)據(jù)通過該參數(shù)傳入并執(zhí)行(該執(zhí)行過程是在UIKit所提供的隊列執(zhí)行,并非主隊列)。該執(zhí)行過程會攜帶一個參數(shù):error。如果更換app圖標成功,那么這個參數(shù)就是nil。如果更換過程中發(fā)生了錯誤,那么該對象會指明錯誤信息,并且app的圖標保持不變。

setAlternateIconName2 Document
(翻譯)使用該方法改變app圖標為主圖標或者可更換的圖標。只有在supportsAlternateIcons的返回值為YES時才能更換。

(翻譯)你必須在Info.plist文件的CFBundleIcons鍵里面聲明可以更換的app圖標(主圖標和可更換圖標)。如果需要獲取關(guān)于可更換圖標的配置信息,請查閱 Information Property List Key Reference 里面有關(guān)CFBundleIcons的描述。

?

文檔中反復提到了Info.plist文件與CFBundleIcons,這是Xcode6之前是用來配置App圖標的老方法,后來有了更完備的Assets.scassets,配置App圖標更簡單與完善了。不過如今該方法再次被搬上臺面,在蘋果內(nèi)部一定也是歷經(jīng)多次“撕逼”后的結(jié)果,為何蘋果急于在10.3而不是11推出該API?為何蘋果不使用Assets.scassets配置可變更的App圖標?我們不得而知,不過相信蘋果后期會對該配置方法做優(yōu)化的。

可變更App圖標的配置方法

官方配置文檔

CFBundleAlternateIcons1 Document
該配置文檔的內(nèi)容較多,我們挑重點羅列下(忽略tvOS部分,下同):

Info.plist是個字典,假設(shè)為NSDictionary *infoPlist。

CFBundleIcons是Info.plist字典里的一個鍵@"CFBundleIcons"。

CFBundleIcons對應(yīng)的value是個字典。
CFBundleIcons里面能夠包含的鍵有:CFBundlePrimaryIcon、CFBundleAlternateIcons、UINewsstandIcon。
讓我們用代碼展示下這個繞口的結(jié)構(gòu):

NSDictionary *infoPlist;
infoPlist = @{
               @"CFBundleIcons" : @{
                                     @"CFBundlePrimaryIcon" : xxx,
                                     @"CFBundleAlternateIcons" : xxx,
                                     @"UINewsstandIcon" : xxx
                                   }
             };

CFBundleAlternateIcons2 Document
這是關(guān)于CFBundleAlternateIcons的配置文檔:

其中有一句話,不仔細思考很難明白:

In iOS, the value of the key is a dictionary. The key for each dictionary entry is the name of the alternate icon
翻譯:

該鍵對應(yīng)的值是字典,每個字典條目的鍵都是備用圖標的名稱。
從這句話中無法很快理清CFBundleAlternateIcons下層的數(shù)據(jù)結(jié)構(gòu)。實際上這句話表達的意思是:

該鍵對應(yīng)的值是字典,這個字典里的每一個鍵對應(yīng)的又是一個個字典,而這些鍵都是備用圖標的名稱。
讓我們把剩余的重點羅列下:

CFBundleAlternateIcons所對應(yīng)的value是個字典(iOS中),假設(shè)為NSDictionary * alertnateIconsDic。
alertnateIconsDic的鍵,都是備用圖標的名字,假設(shè)為@"newAppIcon"和@"newAppIcon2"。
@"newAppIcon"的value是個包含CFBundleIconFiles和UIPrerenderedIcon這兩個鍵的字典。
CFBundleIconFiles的value是字符串或者數(shù)組(數(shù)組內(nèi)容也為字符串)。字符串的內(nèi)容為各尺寸備用圖標的名字。
UIPrerenderedIcon的value是BOOL值。這個鍵值所代表的作用在iOS7之后(含iOS7)已失效,在iOS6中可渲染app圖標為帶高亮效果。所以這個值目前我們可以不用關(guān)心。
讓我們用代碼展示下CFBundleAlternateIcons的value的結(jié)構(gòu):

@"CFBundleAlternateIcons" : @{
                               @"newAppIcon" : @{
                                                 @"CFBundleIconFiles" : @[
                                                                            @"newAppIcon"
                                                                         ],
                                                 @"UIPrerenderedIcon" : NO
                                                },
                               @"newAppIcon2" : @{
                                                 @"CFBundleIconFiles" : @[
                                                                            @"newAppIcon2"
                                                                         ],
                                                 @"UIPrerenderedIcon" : NO
                                                 }
                             }

實際配置文件(Info.plist)

對照著上述的配置文檔,我們實際配置完的Info.plist是這樣子的:

Info.plist1
當然也要拖入對應(yīng)的App圖標:

各種天氣App圖標
不過這里我們好像還少配置了App主圖標,也就是正常情況下我們的圖標。按照文檔所說,我們需要在CFBundleIcons里面配置CFBundlePrimaryIcon這個主圖標對應(yīng)的內(nèi)容,但是實際上,我們還是按照老方法,在Assets.xcassets中配置AppIcon,對應(yīng)尺寸填上對應(yīng)圖片即可。為什么這樣子就可以配置主圖標呢?讓我們來看看某知名電商的ipa(在AppStore上下載的包)內(nèi)的Info.plist(位于Payload/XXXXXX/Info.plist):

知名電商的Info.plist
當然你也可以在你自己App打出的包內(nèi)進行查看,系統(tǒng)其實是會將Assets.xcassets中配置的AppIcon轉(zhuǎn)化為Info.plist中的CFBundlePrimaryIcon。所以我們主圖標的配置方式還是與原先一樣。

其他注意事項:

文件擴展名,如@2x,@3x,要么統(tǒng)一不寫,那么系統(tǒng)會自動尋找合適的尺寸。要寫就需要把每張icon的擴展名寫上,和上圖的格式一樣,在本系列文章的Demo中也有一個單獨的Demo示例如何添加多尺寸icon。
iPad版本如果需要有更換的圖標,需要在CFBundleIcons?ipad同樣設(shè)置一次。
更換圖標后,如何驗證iPhone上使用了多尺寸的圖標?

所有尺寸圖標
打開DynamicAppIcon(帶尺寸)這個Demo。該Demo中,我們在各個尺寸的圖標右上角打個”標記“,然后使用上文介紹的setAlternateIconName:completionHandler:進行圖標更換。更換圖標的同時,我們再做一件事:

// 測試推送上是否使用了20尺寸的圖標
UILocalNotification *noti = [[UILocalNotification alloc] init];
noti.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
noti.alertBody = @"我們看看推送上面的App圖標";
[[UIApplication sharedApplication] scheduleLocalNotification:noti];
這里我們發(fā)送了一個本地通知,一會我們就能看到通知上顯示的是什么圖標了:

本地推送圖標對比
再讓我們?nèi)ettings里面觀察下App圖標:

設(shè)置界面圖標對比
看到圖標的區(qū)別,也就說明了我們在Info.plist里面設(shè)置的多尺寸圖標生效了:

多尺寸Info.Pilst
下一篇

在這篇文章里,你能看到App圖標在運行時被更換了,但是更換的時候會給出一個“擾人”的彈框,該彈框是蘋果爸爸默認加上去的,下一篇就是告訴各位,如何反抗爸爸:去除更換App圖標時的彈框。
iOS動態(tài)更換App圖標(二):無彈框更換App圖標(簡書地址):http://www.lxweimin.com/p/dd5144f8f021

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容