iOS 10.3 如何动态更换App图标

原文链接:http://www.jianshu.com/p/dd5144f8f021

动态更换App图标这件事,在用户里总是存在需求的:有些用户喜欢“美化”自己的手机。至于用户们喜欢美化到什么程度,这得看个人需求。有的用户想定制个性的App图标,那么各大iPhone论坛里都有方法可以不越狱更改App图标;有的用户想让App图标“动”起来(如系统应用时钟),那么不越个狱还真不好办。

不过今天我们想谈谈苹果官方对于动态更换App图标的支持。

本系列文章

iOS动态更换App图标(一):基础使用:http://daiyi.pro/2017/05/01/ChangeYourAppIcons1/
iOS动态更换App图标(二):无弹框更换App图标:http://daiyi.pro/2017/05/01/ChangeYourAppIcons2/
iOS动态更换App图标(三):动态下载App图标进行更换
Demo演示

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

本篇文章对应工程为:DynamicAppIcon(一)

Demo中可以看到,在不重新安装App的情况下,可以实现更新App的图标。但是会弹出一个提示,告知用户当前图标已更换,当然下一篇文章将会突破这个“限制”。

该功能应用的场景

1、白天/夜间模式切换,在切换App主色调同时切换App图标。

2、各类皮肤主题(淘宝就可换肤),附带App图标一块更换。

3、利用App图标表达某种特定功能,如Demo中的,提示当前天气。

4、图标促销提示,如淘宝京东特定节日:11.11、6.18,提前更换App图标。

当然该功能(API)当前只支持iOS10.3以上的系统,所以只能当做一项附加功能来进行使用。下面将详细讲解下如何使用代码来实现此功能。

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代表使用主图标. 完成后的操作将会在任意的后台队列中异步执行; 如果需要更改UI,请确保在主队列中执行.
- (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
(翻译)只有系统允许改变你的app图标时该值才为YES。你需要在Info.plist文件中的CFBundleIcons这个键内声明可更换的app图标。

alternateIconName

alternateIconName Document
(翻译)当系统展示的是你更换后的app图标时,该值即为图标名字(Info.plist中定义的图标名字)。如果展示的是主图标时,这个值为nil。

setAlternateIconName:completionHandler:

setAlternateIconName Document
(翻译)alertnateIconName参数:该参数为需要更换的app图标名字,是在你的Info.plist中的CFBundleAlertnateIcons键里定义的。如果你想显示的是用CFBundlePrimaryIcon键所定义的主图标的话,就传入nil。CFBundleAlertnateIcons与CFBundlePrimaryIcon键都是在CFBundleIcons里面定义的。

(翻译)completionHandler参数:该参数用来处理(更换)结果。当系统尝试更改app的图标后,会将结果数据通过该参数传入并执行(该执行过程是在UIKit所提供的队列执行,并非主队列)。该执行过程会携带一个参数:error。如果更换app图标成功,那么这个参数就是nil。如果更换过程中发生了错误,那么该对象会指明错误信息,并且app的图标保持不变。

setAlternateIconName2 Document
(翻译)使用该方法改变app图标为主图标或者可更换的图标。只有在supportsAlternateIcons的返回值为YES时才能更换。

(翻译)你必须在Info.plist文件的CFBundleIcons键里面声明可以更换的app图标(主图标和可更换图标)。如果需要获取关于可更换图标的配置信息,请查阅 Information Property List Key Reference 里面有关CFBundleIcons的描述。

文档中反复提到了Info.plist文件与CFBundleIcons,这是Xcode6之前是用来配置App图标的老方法,后来有了更完备的Assets.scassets,配置App图标更简单与完善了。不过如今该方法再次被搬上台面,在苹果内部一定也是历经多次“撕逼”后的结果,为何苹果急于在10.3而不是11推出该API?为何苹果不使用Assets.scassets配置可变更的App图标?我们不得而知,不过相信苹果后期会对该配置方法做优化的。

可变更App图标的配置方法

官方配置文档

CFBundleAlternateIcons1 Document
该配置文档的内容较多,我们挑重点罗列下(忽略tvOS部分,下同):

Info.plist是个字典,假设为NSDictionary *infoPlist。

CFBundleIcons是Info.plist字典里的一个键@"CFBundleIcons"。

CFBundleIcons对应的value是个字典。
CFBundleIcons里面能够包含的键有:CFBundlePrimaryIcon、CFBundleAlternateIcons、UINewsstandIcon。
让我们用代码展示下这个绕口的结构:

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

CFBundleAlternateIcons2 Document
这是关于CFBundleAlternateIcons的配置文档:

其中有一句话,不仔细思考很难明白:

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

该键对应的值是字典,每个字典条目的键都是备用图标的名称。
从这句话中无法很快理清CFBundleAlternateIcons下层的数据结构。实际上这句话表达的意思是:

该键对应的值是字典,这个字典里的每一个键对应的又是一个个字典,而这些键都是备用图标的名称。
让我们把剩余的重点罗列下:

CFBundleAlternateIcons所对应的value是个字典(iOS中),假设为NSDictionary * alertnateIconsDic。
alertnateIconsDic的键,都是备用图标的名字,假设为@"newAppIcon"和@"newAppIcon2"。
@"newAppIcon"的value是个包含CFBundleIconFiles和UIPrerenderedIcon这两个键的字典。
CFBundleIconFiles的value是字符串或者数组(数组内容也为字符串)。字符串的内容为各尺寸备用图标的名字。
UIPrerenderedIcon的value是BOOL值。这个键值所代表的作用在iOS7之后(含iOS7)已失效,在iOS6中可渲染app图标为带高亮效果。所以这个值目前我们可以不用关心。
让我们用代码展示下CFBundleAlternateIcons的value的结构:

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

实际配置文件(Info.plist)

对照着上述的配置文档,我们实际配置完的Info.plist是这样子的:

Info.plist1
当然也要拖入对应的App图标:

各种天气App图标
不过这里我们好像还少配置了App主图标,也就是正常情况下我们的图标。按照文档所说,我们需要在CFBundleIcons里面配置CFBundlePrimaryIcon这个主图标对应的内容,但是实际上,我们还是按照老方法,在Assets.xcassets中配置AppIcon,对应尺寸填上对应图片即可。为什么这样子就可以配置主图标呢?让我们来看看某知名电商的ipa(在AppStore上下载的包)内的Info.plist(位于Payload/XXXXXX/Info.plist):

知名电商的Info.plist
当然你也可以在你自己App打出的包内进行查看,系统其实是会将Assets.xcassets中配置的AppIcon转化为Info.plist中的CFBundlePrimaryIcon。所以我们主图标的配置方式还是与原先一样。

其他注意事项:

文件扩展名,如@2x,@3x,要么统一不写,那么系统会自动寻找合适的尺寸。要写就需要把每张icon的扩展名写上,和上图的格式一样,在本系列文章的Demo中也有一个单独的Demo示例如何添加多尺寸icon。
iPad版本如果需要有更换的图标,需要在CFBundleIcons〜ipad同样设置一次。
更换图标后,如何验证iPhone上使用了多尺寸的图标?

所有尺寸图标
打开DynamicAppIcon(带尺寸)这个Demo。该Demo中,我们在各个尺寸的图标右上角打个”标记“,然后使用上文介绍的setAlternateIconName:completionHandler:进行图标更换。更换图标的同时,我们再做一件事:

// 测试推送上是否使用了20尺寸的图标
UILocalNotification *noti = [[UILocalNotification alloc] init];
noti.fireDate = [NSDate dateWithTimeIntervalSinceNow:5];
noti.alertBody = @"我们看看推送上面的App图标";
[[UIApplication sharedApplication] scheduleLocalNotification:noti];
这里我们发送了一个本地通知,一会我们就能看到通知上显示的是什么图标了:

本地推送图标对比
再让我们去Settings里面观察下App图标:

设置界面图标对比
看到图标的区别,也就说明了我们在Info.plist里面设置的多尺寸图标生效了:

多尺寸Info.Pilst
下一篇

在这篇文章里,你能看到App图标在运行时被更换了,但是更换的时候会给出一个“扰人”的弹框,该弹框是苹果爸爸默认加上去的,下一篇就是告诉各位,如何反抗爸爸:去除更换App图标时的弹框。
iOS动态更换App图标(二):无弹框更换App图标(简书地址):http://www.jianshu.com/p/dd5144f8f021

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容