1、iOS 10.3 开放了更换 app 图标的 API,核心方法是下面这个:
-
[[UIApplication sharedApplication] setAlternateIconName:nil completionHandler:^(
-
-
}];
这是官方文档,但是你还需要在
info.plist 里面填一些东西才能让它起作用,这部分官方注释内容在这里。

2、 info.plist 如何填写呢?一时可能搞不清楚如何操作,下面做个实例:


3、具体如下:

-
<key>CFBundleIcons</key>
-
<dict>
-
<key>CFBundleAlternateIcons</key>
-
<dict>
-
<key>newIcon</key>
-
<dict>
-
<key>CFBundleIconFiles</key>
-
<array>
-
<string>newIcon</string>
-
</array>
-
<key>UIPrerenderedIcon</key>
-
<false/>
-
</dict>
-
</dict>
-
<key>CFBundlePrimaryIcon</key>
-
<dict>
-
<key>CFBundleIconFiles</key>
-
<array>
-
<string>Icon60X60</string>
-
</array>
-
</dict>
-
</dict>
如图,Primary Icon
字段写为 Icon60X60 是因为这里 xcassets 里面我只导入了 [email protected] 和 [email protected] 的图片资源,这里选为 60 是因为对于 iPhone,60pt 的图片资源图标所需最高质量,更低分辨率的版本系统会自动压缩以展示。
newIcon
是我的用于替换原生图标的图片资源。文件名需要和 info.plist 中保持一致(注意 info.plist 中用到了两次 "newIcon"),同时这也是你在代码中设置图标时,需要给 API 传入的参数。同样是 [email protected] 和 [email protected] 的图片资源,文件不通过 Assets.xcassets 添加进来,而是直接放到目录中。
如果你需要支持 iPad,建议这里使用 83.5pt(iPad Pro)的图片资源。另外还有些其他关于在 iPad 上替换图标的注意事项,在这里有说明,注意我们这里在
info.plist 里面所用的 key 是 CFBundleIcons
,还有另外一个 key 是 CFBundleIcons~ipad
。
4、替换图标部分的代码:
-
- (void)changeAppIcon
-
{
-
if ([UIApplication sharedApplication].supportsAlternateIcons) {
-
NSLog(@"you can change this app's icon");
-
}else{
-
NSLog(@"you can not change this app's icon");
-
return;
-
}
-
-
NSString *iconName = [[UIApplication sharedApplication] alternateIconName];
-
-
if (iconName) {
-
// change to primary icon
-
[[UIApplication sharedApplication] setAlternateIconName:nil completionHandler:^(NSError * _Nullable error) {
-
if (error) {
-
NSLog(@"set icon error: %@",error);
-
}
-
NSLog(@"The alternate icon's name is %@",iconName);
-
}];
-
}else{
-
// change to alterante icon
-
[[UIApplication sharedApplication] setAlternateIconName:@"newIcon" completionHandler:^(NSError * _Nullable error) {
-
if (error) {
-
NSLog(@"set icon error: %@",error);
-
}
-
NSLog(@"The alternate icon's name is %@",iconName);
-
}];
-
}
-
}
5、最终效果如下:

优化:
很多人就说了,每次都要弹框修改多费劲啊,能不能优化在后台切换icon呢?我的答案是:能!
-
- (void)viewDidLoad {
-
[super viewDidLoad];
-
// Do any additional setup after loading the view, typically from a nib.
-
[self runtimeReplaceAlert];
-
}
-
-
// 利用runtime来替换展现弹出框的方法
-
- (void)runtimeReplaceAlert
-
{
-
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(ox_presentViewController:animated:completion:));
-
// 交换方法实现
-
method_exchangeImplementations(presentM, presentSwizzlingM);
-
});
-
}
-
-
// 自己的替换展示弹出框的方法
-
- (void)ox_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);
-
-
// 换图标时的提示框的title和message都是nil,由此可特殊处理
-
UIAlertController *alertController = (UIAlertController *)viewControllerToPresent;
-
if (alertController.title == nil && alertController.message == nil) { // 是换图标的提示
-
return;
-
} else {// 其他提示还是正常处理
-
[self ox_presentViewController:viewControllerToPresent animated:flag completion:completion];
-
return;
-
}
-
}
-
-
[self ox_presentViewController:viewControllerToPresent animated:flag completion:completion];
-
}
优化后的效果如下:
