前几天给项目上了个新版本,然后客户那边突然想改app图标了,这就意味着我需要从新上架个版本。于是上网找了点资料,看看有没有方法动态更改的。发现iOS 10.3之后苹果提供了更改app图标的方法,不过需要在info.plist中事先配置。也就是说只能更换为事先配置好的app图标。虽然不能满足动态更改的需求,但是在某些特殊需求还是有用处的。比如app在特定日子更换图标等等。,
1、相应api
1.判断程序是否可以更改icon
[UIApplication sharedApplication].supportsAlternateIcons
2.获取当前app使用的icon名称,nil为资源文件夹中配置的图片名称
//更换的图标名称,如果为nil则为原始图标名称,
NSString *name = [[UIApplication sharedApplication] alternateIconName];
3.设置app的icon
[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"set icon error: %@",error);
}
}];
2、配置info.plist
假设现在有个需求,需要app根据当前的天气情况更换相应的图标。
1.将相应的资源图片拖到项目中
注意:图片资源直接拖到工程目录就行,不能放到资源文件夹中,否则icon将不能更换成功。这可能和api实现有关,拖入到资源文件夹中图片可以通过imageWithName:获得,而工程中的图片只能通过路径获取。
1.配置info.plist
<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>cloud</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>cloud</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>snow</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>snow</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>Icon60X60</string>
</array>
</dict>
</dict>
info.plist加入以下配置,第一个rain为[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:nil]方法中的iconName参数,第二个rain为工程中图片的名字。第一个rain可以随意,也可以和图片名称不一样,只要保持一一对应就行。但是传入的iconName一定要在plist中配置,否则会报错。调用设置icon api时应该是先从配置文件中查询这个iconName,然后再找到相应图片更换。
3、代码实现
-(IBAction)changeIcon:(UIButton *)sender {
if (![UIApplication sharedApplication].supportsAlternateIcons) {
NSLog(@"不能更换icon");
return;
}
switch (sender.tag) {
case 101:
//晴天
[self setIconWithName:nil];
break;
case 102:
//雨
[self setIconWithName:@"rain"];
break;
case 103:
//多云
[self setIconWithName:@"cloud"];
break;
//雪
case 104:
[self setIconWithName:@"snow"];
break;
default:
break;
}
}
-(void)setIconWithName:(NSString *)iconName{
//更换的图标名称,如果为nil则为原始图标名称,
NSString *name = [[UIApplication sharedApplication] alternateIconName];
if ((!iconName&&!name) || [name isEqualToString:iconName]) {
return;
}
[[UIApplication sharedApplication] setAlternateIconName:iconName completionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"set icon error: %@",error);
}
}];
NSLog(@"The alternate icon's name is %@",iconName);
}
效果如下:
3、代码优化,弹出框去除
如果更改icon的控制器为keyWindow的rootViewController中,或者为导航控制器作为rootViewController的子控制器时,更改icon时会出现上图弹出框。如果是rootViewController模态出来的一个控制器,则不会弹出弹出框。
去除弹出框
弹出框的出现主要是系统调用了presentViewController:animated:completion:这个方法,如果我们拦截这个方法,在这种情况下不调用,就可以去掉这个可恶的弹出框了。这时候就需要用到一些runTime的知识了代码如下:
#import "UIViewController+changeIcon.h"
#import <objc/runtime.h>
@implementation UIViewController (changeIcon)
+(void)load{
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];
}
@end
自定义一个新方法,如果模态跳转的控制为UIAlertController,并且title和message都是nil则可以判断特殊情况(包括更换icon),return掉。其他情况正常跳转。
总结
这个方法只能对已知图片进行更改,如果需要更换新图标还是需要从新上架。最后附上代码github