动态修改app的图标,就是在不从新安装app的状况下,能够修改当前的icon图标;在某些状况下,是有这个需求的;例如,能够更换主题的app中,通常都会有一套完整的主题包含相应的icon;还有就是一些节日主题的icon或者促销的icon,例如淘宝、京东等的节日icon。ios
在iOS 10.3以后,苹果官方提供了相关的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复制代码
方法很简单,可是使用以前须要进行一些配置:微信
动态修改的icon不能放在 Assets.xcassets 里,可是正常的主icon仍是能够在这里设置的,也能够按下面的方法来设置;
首先,把须要修改的icon放在一个文件夹内:app
其文件夹内是这样的异步
这里每种icon我只放了一个,若是有多个尺寸的icon,也能够直接全放进去:学习
而后,文件夹会变成这样:测试
这里的icon名称只须要和下面配置一致便可ui
在info.plist中右键 -> Add Row :
输入Icon... 会有提示,选择Icon files(iOS 5)atom
这时候,内容是这样的:spa
这里的Icon files(iOS 5)是个字典,其中可包含的Key值有CFBundlePrimaryIcon -> Primary Icon
CFBundleAlternateIcons
UINewsstandIcon -> Newsstand Icon
这里的Primary Icon是设置app的主icon,能够在这里的Icon files数组内添加,有多个的话,依次添加,也能够这里不用填写,直接在Assets.xcassets 里配置;
下面的Newsstand Icon,暂时用不到,不用管,也能够删除。
在 Icon files(iOS 5)内添加一个Key: CFBundleAlternateIcons ,类型为字典,在这个字典里配置咱们全部须要动态修改的icon:键为icon的名称,值为一个字典(这个字典里包含两个键:CFBundleIconFiles,其值类型为Array,内容为icon的名称;UIPrerenderedIcon,其值类型为bool,内容为NO,也能够不加此key),例如:
把第一步中添加的图片所有添加进来就是这样的:
到此,info.plist的配置即完成了;
或者将info.plist文件以 Source code 方式打开,添加如下代码:
<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>rain</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>rain</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>snow</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>snow</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>sunshine</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>sunshine</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>cloudy</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>cloudy</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>UINewsstandIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UINewsstandBindingType</key>
<string>UINewsstandBindingTypeMagazine</string>
<key>UINewsstandBindingEdge</key>
<string>UINewsstandBindingEdgeLeft</string>
</dict>
</dict>复制代码
若是是添加了多个尺寸icon,也要在这里分别配置,以上面添加的sunshine图标为例:
使用的时候仍是使用sunshine进行赋值便可!
配置完成后,代码部分就比较简单了:
- (void)changeAppIconWithName:(NSString *)iconName {
if (![[UIApplication sharedApplication] supportsAlternateIcons]) {
return;
}
if ([iconName isEqualToString:@""]) {
iconName = nil;
}
[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"更换app图标发生错误了 : %@",error);
}
}];
}复制代码
在须要修改icon的地方调用这个方法,并把相应的icon名称传进去便可:
- (IBAction)snow:(id)sender {
[self changeAppIconWithName:@"snow"];
}
- (IBAction)rain:(id)sender {
[self changeAppIconWithName:@"rain"];
}
- (IBAction)cloudy:(id)sender {
[self changeAppIconWithName:@"rain"];
}
- (IBAction)sunshine:(id)sender {
[self changeAppIconWithName:@"sunshine"];
}复制代码
示意图:
iPad的动态图标设置和上面步骤基本同样,有的文章说是将 CFBundleIcons 改成 CFBundleIcons~ipad,即:
可是,在测试中发现,使用上面的key值也是能够实现动态改变的,即不作任何修改,iPhone和iPad使用相同的配置,即:CFBundleIcons。
从上面的示意图能够发现,在设置icon的时候,会有个系统弹框,这样有时候会不太友好,咱们可使用Runtime,对UIViewController进行扩展来隐藏这个弹框:
// UIViewController+LQNoPresent.h
#import <UIKit/UIKit.h>
@interface UIViewController (LQNoPresent)
@end
// UIViewController+LQNoPresent.m
#import "UIViewController+LQNoPresent.h"
#import <objc/runtime.h>
@implementation UIViewController (LQNoPresent)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method presentM = class_getInstanceMethod(self.class, @selector(presentViewController:animated:completion:));
Method presentSwizzlingM = class_getInstanceMethod(self.class, @selector(lq_presentViewController:animated:completion:));
method_exchangeImplementations(presentM, presentSwizzlingM);
});
}
- (void)lq_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
if ([viewControllerToPresent isKindOfClass:[UIAlertController class]]) {
// NSLog(@"title : %@",((UIAlertController *)viewControllerToPresent).title);
// NSLog(@"message : %@",((UIAlertController *)viewControllerToPresent).message);
UIAlertController *alertController = (UIAlertController *)viewControllerToPresent;
if (alertController.title == nil && alertController.message == nil) {
return;
}
}
[self lq_presentViewController:viewControllerToPresent animated:flag completion:completion];
}
@end复制代码
这样在切换图标的时候就没有系统的弹框了:
此文章来源于第三方转载
小编这呢,给你们推荐一个优秀的iOS交流平台,平台里的伙伴们都是很是优秀的iOS开发人员,咱们专一于技术的分享与技巧的交流,你们能够在平台上讨论技术,交流学习。欢迎你们的加入(想要进入的可加小编微信)。 13142121176