swift动态替换应用图标


近期需求中有一个需要动态替换应用图标的功能,查了下相关资料:在ios10.3之前是无法实现的,你只能提前发布拥有新应用图标的版本,来满足某个节日;节后,再发布一版,改回图标。从ios10.3开始,苹果提供了动态替换应用图标的API。至于api说明,请看苹果帮助,下面我们就按步骤实现这个功能。

1、准备好替换图标,我准备了春、夏、秋、冬四种替换图标,详见Demo。

2、在项目plist文件的根字典中添加如下:

<dict>
	<key>CFBundleIcons</key>
	<dict>
		<key>CFBundleAlternateIcons</key>
		<dict>
			<key>春</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>春Icon-Small-20</string>
					<string>春Icon-60</string>
					<string>春Icon-Small-29</string>
					<string>春Icon-Small-40</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>夏</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>夏Icon-Small-20</string>
					<string>夏Icon-60</string>
					<string>夏Icon-Small-29</string>
					<string>夏Icon-Small-40</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>秋</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>秋Icon-Small-20</string>
					<string>秋Icon-60</string>
					<string>秋Icon-Small-29</string>
					<string>秋Icon-Small-40</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>冬</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>冬Icon-Small-20</string>
					<string>冬Icon-60</string>
					<string>冬Icon-Small-29</string>
					<string>冬Icon-Small-40</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
		</dict>
	</dict>

即下图所示


3、在视图控制器中添加五个按钮:"春"用于替换春字图标,"夏"用于替换夏字图标, "秋"用于替换秋字图标, "冬"用于替换冬字图标,按钮"原始"用于恢复原始标, 点击实现如下:
    
    /// 春天图标
    @IBAction func 春Clicked(_ sender: UIButton) {
        
        if #available(iOS 10.3, *) {
            if UIApplication.shared.supportsAlternateIcons {
                print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                //当前显示的是原始图标
                UIApplication.shared.setAlternateIconName("春", completionHandler: { (error: Error?) in
                print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                })
            }
        }
    }
    
    /// 夏天图标
    @IBAction func 夏Clicked(_ sender: UIButton) {
        if #available(iOS 10.3, *) {
            if UIApplication.shared.supportsAlternateIcons {
                print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                //当前显示的是原始图标
                UIApplication.shared.setAlternateIconName("夏", completionHandler: { (error: Error?) in
                    print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                })
            }
        }
    }
    
    /// 秋天图标
    @IBAction func 秋Clicked(_ sender: UIButton){
        if #available(iOS 10.3, *) {
            if UIApplication.shared.supportsAlternateIcons {
                print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                //当前显示的是原始图标
                UIApplication.shared.setAlternateIconName("秋", completionHandler: { (error: Error?) in
                    print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                })
            }
        }
    }
    
    /// 冬天图标
    @IBAction func 冬Clicked(_ sender: UIButton){
        if #available(iOS 10.3, *) {
            if UIApplication.shared.supportsAlternateIcons {
                print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                //当前显示的是原始图标
                UIApplication.shared.setAlternateIconName("冬", completionHandler: { (error: Error?) in
                    print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                })
            }
        }
    }
    
    
    /// 原始图标
    @IBAction func primaryClicked(_ sender: UIButton){
        if #available(iOS 10.3, *) {
            if UIApplication.shared.supportsAlternateIcons {
                print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                //当前显示的是原始图标
                UIApplication.shared.setAlternateIconName(nil, completionHandler: { (error: Error?) in
                    print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
                })
            }
        }
    }

4、运行程序,点击按钮,会出现如下alert, 你切到主屏幕界面会发现应用图标已替换。

    

5、对于每次替换图标就弹出个提示,我个人觉得不太爽,需要想办法去掉。替换原理是:因为alert是通过UIViewController的present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil)方法弹出的,我们只要修改该消息的实现,他就不会弹出了。这里就要用到OC的swizzling method技术,swift默认是静态绑定方法实现的,需要对想拥有动态消息特征的方法添加 @objc dynamic修饰符。这里只谈实现,至于OC的相关技术大家自行了解。
在视图控制器中添加如下三个方法:

    //利用runtime指定方法实现
    func runtimeRemoveAlert() -> Void {        
        if let presentM = class_getInstanceMethod(type(of: self), #selector(present(_:animated:completion:))),
            let presentSwizzlingM = class_getInstanceMethod(type(of: self), #selector(temporary_present(_:animated:completion:))){
            method_exchangeImplementations(presentM, presentSwizzlingM)
        }
    }
    //利用runtime恢复方法实现
    func runtimeResetAlert() -> Void {
        if let presentM = class_getInstanceMethod(type(of: self), #selector(present(_:animated:completion:))),
            let presentSwizzlingM = class_getInstanceMethod(type(of: self), #selector(temporary_present(_:animated:completion:))){
            method_exchangeImplementations(presentM, presentSwizzlingM)
        }
    }
    
    //在自己实现中特殊处理
    @objc dynamic func temporary_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil){
        if viewControllerToPresent.isKind(of: UIAlertController.self) {
            if let alertController = viewControllerToPresent as? UIAlertController{
                //通过判断title和message都为nil,得知是替换icon触发的提示。
                if alertController.title == nil && alertController.message == nil {
                    return;
                }
            }
        }
        
        self.temporary_present(viewControllerToPresent, animated: flag, completion: completion)
    }


6、在视图控制器中如下调用:
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        self.runtimeRemoveAlert()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        self.runtimeResetAlert()
    }
    
7、为了验证对正常的alert展示没有影响,在视图控制器中添加“alert”按钮,对于点击处理如下:
    @IBAction func alertClicked(_ sender: UIButton) {
        let title = "title"
        let msg = "message"
        let cancelTitle = "cancel"
        let okTitle = "ok"
        
        let alertController = UIAlertController(title: title, message: msg, preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { (action: UIAlertAction) in
            print("cancel")
        }
        
        let okAction = UIAlertAction(title: okTitle, style: .default) { (action: UIAlertAction) in
            print("ok")
        }
        
        alertController.addAction(cancelAction)
        alertController.addAction(okAction)
        
        self.present(alertController, animated: true) {
            
        }
    }
    
    
 8、运行发现替换图标时,提示已不再出现;但是聪明的你会发现,这时 completionHandler没被调用,因为他是前面展示的Alert后,点击OK按钮触发的。

 工程下载地址https://github.com/qianlima210210/AlternateIconsDemo.git
 





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值