惊人开发技巧:轻松更换 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新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!

相关推荐
DisonTangor11 小时前
苹果发布iOS 18.2首个公测版:Siri接入ChatGPT、iPhone 16拍照按钮有用了
ios·chatgpt·iphone
- 羊羊不超越 -11 小时前
App渠道来源追踪方案全面分析(iOS/Android/鸿蒙)
android·ios·harmonyos
2401_865854881 天前
iOS应用想要下载到手机上只能苹果签名吗?
后端·ios·iphone
HackerTom2 天前
iOS用rime且导入自制输入方案
ios·iphone·rime
良技漫谈2 天前
Rust移动开发:Rust在iOS端集成使用介绍
后端·程序人生·ios·rust·objective-c·swift
2401_852403552 天前
高效管理iPhone存储:苹果手机怎么删除相似照片
ios·智能手机·iphone
星际码仔2 天前
【动画图解】是怎样的方法,能被称作是 Flutter Widget 系统的核心?
android·flutter·ios
emperinter2 天前
WordCloudStudio:AI生成模版为您的文字云创意赋能 !
图像处理·人工智能·macos·ios·信息可视化·iphone
关键帧Keyframe2 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
pb82 天前
引入最新fluwx2.5.4的时候报错
flutter·ios