PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛
起因
一年一度的春节就要来了,各大厂商APP都已经把Icon更换为春节图片,奇怪的是,APP没有更新怎么Logo会变?今天就来说说这个技术点(淘宝、支付宝已经用了几年了)。
需求
- 修改App内Icon
- 静默切换
系统API
首先,这个技术仅在 iOS 10.3以上才可用
先展示系统API
@available(iOS 10.3, *)
open var supportsAlternateIcons: Bool { get }//是否支持备用logo
// Pass `nil` to use the primary application icon. The completion handler will be invoked asynchronously on an arbitrary background queue; be sure to dispatch back to the main queue before doing any further UI work.
@available(iOS 10.3, *)
open func setAlternateIconName(_ alternateIconName: String?, completionHandler: ((Error?) -> Void)? = nil)//设置备用logo
// If `nil`, the primary application icon is being used.
@available(iOS 10.3, *)
open var alternateIconName: String? { get }//当前logo名称
网上找到的实现方式
网上查了下资料,先说下网上的实现步骤:
1、添加icon图片
图片放在工程目录下,而不是系统的资源管理Assets文件下
2、info.plist配置
在info.plist中添加关键字:Icon files(iOS 5)
添加成功后是这个样子
似乎没有网上说的CFBundleAlternateIcons关键字
没有那就手动加吧,按照帖子加上之后的样子
可以看到上面的item对应的就是icon 的名字,这里可以添加多个,以备各种设备的适配
这样配置就完成了,注意记住对应的名称
3、调用代码
上代码
if UIApplication.shared.supportsAlternateIcons{
let str = UIApplication.shared.alternateIconName
if str != "sunshine"{
UIApplication.shared.setAlternateIconName("sunshine") { error in
if error != nil{
print("\(String(describing: error))")
}
}
}
}
注意:sunshine对应上面配置的名称
4、网上资料实现方式总结
到这里就可以看到app logo被替换了,会一个弹窗提示,这个后面说,先说这个流程有没有觉得很麻烦呀,而且资源管理放在工程目录下,作为以优雅著称的苹果会容忍这样的问题
,本着探索的精神,看了下工程配置
更优雅的实现方式
在Build Settings 下找到了Alternate App Icon Sets 配置项,这不就是备用logo集合嘛,接下来开始为的表演
1、现在工程资源文件夹下新建两个AppIcon 资源夹,分别命名为sat 和 gua,然后按照配置常用logo的方式,添加资源
2、在Build Settings 下的Alternate App Icon Sets项右边双击,添加sat gua两项
3、使用上面的设置代码
OK,至此完美收工。很简单有木有!!!!
弹窗问题处理
但是大家在使用时,肯定也发现了问题,这样设置会有一个提示弹窗,没有淘宝、支付宝那么优雅,本着追求极致的态度,我们继续研究,发现弹窗就是UIAlertController,并且是没有title和message的
这个时候就要使用魔法了Runtime
登场
Swift语言弹窗处理代码
直接上代码:Swift
extension UIViewController{
public class func initializeMethod(){
if self != UIViewController.self{
return
}
DispatchQueue.once(token: "ChangeIcon") {
let orignal = class_getInstanceMethod(self, #selector(UIViewController.present(_:animated:completion:)))
let swizzling = class_getInstanceMethod(self, #selector(UIViewController.ssl_present(_:animated:completion:)))
if let old = orignal, let new = swizzling{
method_exchangeImplementations(old, new)
}
}
}
@objc private func ssl_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
if viewControllerToPresent is UIAlertController{
let vc = viewControllerToPresent as! UIAlertController
if vc.title == nil && vc.message == nil{
return
}
}
self.ssl_present(viewControllerToPresent, animated: flag, completion: completion)
}
}
extension DispatchQueue{
private static var _onceTracker = [String]()
public class func once(token: String, block:()->()){
objc_sync_enter(self)
defer{
objc_sync_exit(self)
}
if _onceTracker.contains(token){
return
}
_onceTracker.append(token)
block()
}
}
启用:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIViewController.initializeMethod()
return true
}
OC语言弹窗处理代码
不忘老朋友,网上找的ObjectC 代码参考地址
// UIViewController+LQNoPresent.h
#import <UIKit/UIKit.h>
@interface UIViewController (LQNoPresent)
@end
#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
到这里就结束了,需求完美结束,如有不足,欢迎大家指正!!!
本文Swift参考:iOS动态更换Icon的全过程记录