动态更换App图标这件事,在用户里老是存在需求的:有些用户喜欢“美化”本身的手机。至于用户们喜欢美化到什么程度,这得看我的需求。有的用户想定制个性的App图标,那么各大iPhone论坛里都有方法能够不越狱更改App图标;有的用户想让App图标“动”起来(如系统应用时钟),那么不越个狱还真很差办。html
不过今天咱们想谈谈苹果官方对于动态更换App图标的支持。ios
Demo地址:github.com/maybeisyi/C…git
本篇文章对应工程为:DynamicAppIcon(一)github
Demo中能够看到,在不从新安装App的状况下,能够实现更新App的图标。可是会弹出一个提示,告知用户当前图标已更换,固然下一篇文章将会突破这个“限制”。objective-c
一、白天/夜间模式切换,在切换App主色调同时切换App图标。api
二、各种皮肤主题(淘宝就可换肤),附带App图标一块更换。数组
三、利用App图标表达某种特定功能,如Demo中的,提示当前天气。xcode
四、图标促销提示,如淘宝京东特定节日:11.十一、6.18,提早更换App图标。数据结构
固然该功能(API)当前只支持iOS10.3以上的系统,因此只能当作一项附加功能来进行使用。下面将详细讲解下如何使用代码来实现此功能。app
@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,翻译以下:
(翻译)只有系统容许改变你的app图标时该值才为YES。你须要在Info.plist文件中的CFBundleIcons这个键内声明可更换的app图标。
(翻译)当系统展现的是你更换后的app图标时,该值即为图标名字(Info.plist中定义的图标名字)。若是展现的是主图标时,这个值为nil。
(翻译)alertnateIconName参数:该参数为须要更换的app图标名字,是在你的Info.plist中的CFBundleAlertnateIcons键里定义的。若是你想显示的是用CFBundlePrimaryIcon键所定义的主图标的话,就传入nil。CFBundleAlertnateIcons与CFBundlePrimaryIcon键都是在CFBundleIcons里面定义的。
(翻译)completionHandler参数:该参数用来处理(更换)结果。当系统尝试更改app的图标后,会将结果数据经过该参数传入并执行(该执行过程是在UIKit所提供的队列执行,并不是主队列)。该执行过程会携带一个参数:error。若是更换app图标成功,那么这个参数就是nil。若是更换过程当中发生了错误,那么该对象会指明错误信息,而且app的图标保持不变。
(翻译)使用该方法改变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图标?咱们不得而知,不过相信苹果后期会对该配置方法作优化的。
该配置文档的内容较多,咱们挑重点罗列下(忽略tvOS部分,下同):
Info.plist是个字典,假设为NSDictionary *infoPlist
。
CFBundleIcons是Info.plist字典里的一个键@"CFBundleIcons"
。
让咱们用代码展现下这个绕口的结构:
NSDictionary *infoPlist;
infoPlist = @{
@"CFBundleIcons" : @{
@"CFBundlePrimaryIcon" : xxx,
@"CFBundleAlternateIcons" : xxx,
@"UINewsstandIcon" : xxx
}
};复制代码
这是关于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下层的数据结构。实际上这句话表达的意思是:
该键对应的值是字典,这个字典里的每个键对应的又是一个个字典,而这些键都是备用图标的名称。
让咱们把剩余的重点罗列下:
NSDictionary * alertnateIconsDic
。@"newAppIcon"
和@"newAppIcon2"
。@"newAppIcon"
的value是个包含CFBundleIconFiles和UIPrerenderedIcon这两个键的字典。让咱们用代码展现下CFBundleAlternateIcons的value的结构:
@"CFBundleAlternateIcons" : @{
@"newAppIcon" : @{
@"CFBundleIconFiles" : @[
@"newAppIcon"
],
@"UIPrerenderedIcon" : NO
},
@"newAppIcon2" : @{
@"CFBundleIconFiles" : @[
@"newAppIcon2"
],
@"UIPrerenderedIcon" : NO
}
}复制代码
对照着上述的配置文档,咱们实际配置完的Info.plist是这样子的:
固然也要拖入对应的App图标:
不过这里咱们好像还少配置了App主图标,也就是正常状况下咱们的图标。按照文档所说,咱们须要在CFBundleIcons里面配置CFBundlePrimaryIcon这个主图标对应的内容,可是实际上,咱们仍是按照老方法,在Assets.xcassets中配置AppIcon,对应尺寸填上对应图片便可。为何这样子就能够配置主图标呢?让咱们来看看某知名电商的ipa(在AppStore上下载的包)内的Info.plist(位于Payload/XXXXXX/Info.plist):
固然你也能够在你本身App打出的包内进行查看,系统实际上是会将Assets.xcassets中配置的AppIcon转化为Info.plist中的CFBundlePrimaryIcon。因此咱们主图标的配置方式仍是与原先同样。
打开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里面设置的多尺寸图标生效了:
在这篇文章里,你能看到App图标在运行时被更换了,可是更换的时候会给出一个“扰人”的弹框,该弹框是苹果爸爸默认加上去的,下一篇就是告诉各位,如何反抗爸爸:去除更换App图标时的弹框。
iOS动态更换App图标(二):无弹框更换App图标(掘金地址)