惊人开发技巧:轻松更换 App 图标,无需系统弹窗!

这里每天分享一个 iOS 的新知识,快来关注我吧

前言

之前在文章中介绍过如何在 App 中设置不同的 Logo,App 的图标就是我们的门面,所以好看的 Logo 非常重要。在一些场景下,用户可能会想设置不同的 Logo,比如用户可以在某些节日(比如春节)设置特别的 Logo,另外还可以在 App 内为高级用户定制不同的 Logo。

为应用配置多 Logo

不知道大家是否还记得,使用系统方法设置不同 Logo 的时候会有一个系统弹窗:

那么如何避免这个弹窗的弹出呢?今天就来讲讲这个技术点。

如何避免系统弹窗?

系统的 UIAlertController 是通过一个控制器 present 出来的,那么有一个思路就是写一个透明的 ViewController,在设置图标时先把这个透明的 VC 弹出来,然后重写 present(_:,:,:) 方法,在其中直接自己 dismiss 掉:

首先是这个透明控制器的实现:

swift 复制代码
class TransparentViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 添加一个透明背景视图
        let backgroundView = UIView()
        backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.0)
        backgroundView.frame = view.bounds
        view.addSubview(backgroundView)
    }
    
    override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
        // 当系统想要调用弹窗时直接 dismiss 掉
        dismiss(animated: false)
    }
}

然后,在实现设置图标的相关代码

swift 复制代码
guard UIApplication.shared.supportsAlternateIcons else { 
    // 不支持
    return
}

let transparentVC = TransparentViewController()
transparentVC.modalPresentationStyle = .overFullScreen
self.present(transparentVC, animated: false) {
    UIApplication.shared
        .setAlternateIconName(selectName) { error in
        if let error {
            print("设置 App Icon 出错: \(error)")
        } else {
            print("App Icon 设置成功")
        }
    }
}

这样的话,当图标设置成功,系统想通过当前控制器调用 present 方法弹出系统弹窗时,我们直接把当前控制器 dismiss 掉,这样就不会有系统弹窗了。

有没有更简单的方法

我查了一些资料,有个方法更简单,就是利用私有方法,当系统调用 setAlternateIconName 方法设置图标时,底层实际上调用了一个 _setAlternateIconName 的私有方法,我们只需要通过反射或者重新实现这个方法来调用即可。

1、反射

swift 复制代码
func setApplicationIconName(_ iconName: String?) {
    if UIApplication.shared.responds(to: #selector(getter: UIApplication.supportsAlternateIcons)) && UIApplication.shared.supportsAlternateIcons {
        
        typealias setAlternateIconName = @convention(c) (NSObject, Selector, NSString?, @escaping (NSError) -> ()) -> ()
        
        let selectorString = "_setAlternateIconName:completionHandler:"
        
        let selector = NSSelectorFromString(selectorString)
        let imp = UIApplication.shared.method(for: selector)
        let method = unsafeBitCast(imp, to: setAlternateIconName.self)
        method(UIApplication.shared, selector, iconName as NSString?, { error in
            if let error {
                print("设置 App Icon 出错: \(error)")
            } else {
                print("App Icon 设置成功")
            }
        })
    }
}

这种方式是通过字符串反射 _setAlternateIconName:completionHandler: 方法来实现的,这种方式也有网友经过实验已经通过了审核,所以不必担心过审问题。

2、定义这个私有方法

因为不使用反射的话我们是无法调用这个私有函数的,因为未声明,另一个思路是使用 OC 的头文件来声明这个方法,这样我们就可以直接调用了,创建一个 OC 的头文件,给系统的 UIApplication 写一个分类,来声明这个方法:

objectivec 复制代码
#import <UIKit/UIKit.h>

@import UIKit;

@interface UIApplication (UIApplication_Private)

- (void)_setAlternateIconName:(nullable NSString *)alternateIconName completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler;

@end

这样,我们只需要在系统方法前增加一个下划线来调用就行了。

swift 复制代码
UIApplication.shared
    ._setAlternateIconName(selectName) { error in
        if let error {
            print("设置 App Icon 出错: \(error)")
        } else {
            print("App Icon 设置成功")
        }
    }

这种反射的方法相对来说不是很保险,一方面是过审问题,现在能过审不代表永远能过审,另一方面系统的私有方法随着系统的迭代可能会发生变化,到时候就会发生崩溃或者无效等问题,因此优先推荐第一种方法来设置。

这里每天分享一个 iOS 的新知识,快来关注我吧

本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
幸福回头4 小时前
ms-swift 代码推理数据集
llm·swift
若水无华14 小时前
fiddler 配置ios手机代理调试
ios·智能手机·fiddler
不二狗14 小时前
每日算法 -【Swift 算法】Two Sum 问题:从暴力解法到最优解法的演进
开发语言·算法·swift
Aress"15 小时前
【ios越狱包安装失败?uniapp导出ipa文件如何安装到苹果手机】苹果IOS直接安装IPA文件
ios·uni-app·ipa安装
Jouzzy1 天前
【iOS安全】Dopamine越狱 iPhone X iOS 16.6 (20G75) | 解决Jailbreak failed with error
安全·ios·iphone
瓜子三百克1 天前
采用sherpa-onnx 实现 ios语音唤起的调研
macos·ios·cocoa
左钦杨1 天前
IOS CSS3 right transformX 动画卡顿 回弹
前端·ios·css3
努力成为包租婆1 天前
SDK does not contain ‘libarclite‘ at the path
ios
安和昂2 天前
【iOS】Tagged Pointer
macos·ios·cocoa
I烟雨云渊T2 天前
iOS 阅后即焚功能的实现
macos·ios·cocoa