iOS开发之动态切换APP图标

iOS开发之动态切换APP图标

1. 概述

最近接手的一个项目中,涉及到了切换APP图标的功能,查阅文档后发现是iOS10.3之后才有的功能,本篇文章就记录一下如果切换APP图标的功能,以备日后使用。

2. 切换APP图标的API

extension UIApplication {
    // 判断是否支持切换APP图标的功能。
    @available(iOS 10.3, *)
    open var supportsAlternateIcons: Bool { get }
    // 设置APP图标,如果alternateIconName传nil,那么默认切换到主APP图标,completionHandler回调在子线程中执行,如果里面要执行UI相关的操作,还需切换到主线程。
    @available(iOS 10.3, *)
    open func setAlternateIconName(_ alternateIconName: String?, completionHandler: ((Error?) -> Void)? = nil)
    // 当前APP使用的图标,如果返回nil,则说明使用的是主图标。
    @available(iOS 10.3, *)
    open var alternateIconName: String? { get }

单纯从API上看,其实还是很容易的,只需要一个setAlternateIconName的方法。但是个人感觉配置起来还是有些小麻烦的。

3. 准备APP图标

这一步完全不需要开发人员去做,有美工小姐姐呢,但是笔者还是认为有必要记录一下。


准备好的其他图案的APP图标,全部放到一个文件夹内,名字随便起,这里面,可以准备一套APP图标,也可以准备几个有针对性的。


这个文件夹需要放到项目目录下面,项目的主图标放在了Assets.xcassets下面的AppIcon.appiconset里面(一般也没人会改这个主图标的位置)。


下面看一下我的配置:

在这里我准备Icon2和Icon3两套APP图标,有点奢侈了。

4. 配置Info.plist

这一步绝对是必不可少的。


苹果的文档要求我们在Info.plist里面配置一个叫CFBundleIcons的key。


CFBundleIcons (Dictionary - iOS, tvOS) contains information about all of the icons used by the app. This key allows you to group icons based on their intended usage and specify multiple icon files together with specific keys for modifying the appearance of those icons.This dictionary can contain the following keys:


4.1 CFBundleIcons
CFBundleIcons是一个字典,包含了App所有的图标信息,我们可以在这里根据不同的key在设置再设置不同的APP图标。并且这个字典里面还包含了一些相关的key,具体来看一下:

Keys Description
CFBundlePrimaryIcon 这个key里面配置了App主图标的一些信息。字典类型。
CFBundleAlternateIcons 这个key里面配置了App备用图标的一些信息,也就是我们要切换的图标的信息。字典类型。
UINewsstandIcon 这个key里面配置了一些用于报刊和杂志类的图标,暂时用不上,本文将不做具体阐述。字典类型。

4.2 CFBundlePrimaryIcon

Key Value Description
CFBundleIconName String (推荐在iOS11及以后版本使用)项目中asset的名称,用来显示APP的图标。如果使用了这个key,那么在CFBundleIconFiles项下面至少要配置一项,以便在非iOS系统,像构造器以及MDM解决方案下,能够正常显示。
CFBundleIconFiles Array of strings (如果是iOS10及以前的版本)数组中的每个字段都包含了图标的名称。可以包含多个不同只存的图标,以便更好的支持iPhone,iPad以及其他设备。对于这些包含尺寸的图标,需将其放在APP的bundle中(项目目录下面,非asset文件里面)。
UIPrerenderedIcon Boolean 这个key表示图标文件是否有光亮效果。如果图标已经有光亮效果了,那么请配置这个key,并设置为YES,这样系统就不会再加光亮效果了。如果没有配置这个key,或者配置了且设置为NO,那么系统将会为CFBundleIconFiles字典下面配置的所有图标添加光亮效果。

在APP主图标配置这块,可以不配置这个CFBundlePrimaryIcon,完全可以使用项目里面asset中的AppIcon.appiconset来配置所有的图片,并修改项目—>target—>App Icons and Launch Images—>App Icons Source为指定的AppIcon。如下图:

另外,如果想使用CFBundlePrimaryIcon来配置APP主图标,那么可以将所有icon的图片放到一个文件夹下,并放到项目目录下面,然后配置Info.plist如下图:

并修改项目—>target—>App Icons and Launch Images—>App Icons Source为指定的Don't use asset catalogs,另外请注意这里没有配置CFBundleIconName。

Key Value Description
CFBundleIconFiles Array of strings (必填)数组中的每个字段都包含了图标的名称。可以包含多个不同只存的图标,以便更好的支持iPhone,iPad以及其他设备。对于这些包含尺寸的图标,需将其放在APP的bundle中(项目目录下面,非asset文件里面)。
UIPrerenderedIcon Boolean 这个key表示图标文件是否有光亮效果。如果图标已经有光亮效果了,那么请配置这个key,并设置为YES,这样系统就不会再加光亮效果了。如果没有配置这个key,或者配置了且设置为NO,那么系统将会为CFBundleIconFiles字典下面配置的所有图标添加光亮效果。

作者:iOS发呆君
链接: juejin.cn/post/69049007
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

CFBundleAlternateIcons可以理解为是备选图标,配置起来也没有那么复杂,如下图所示:

上图中在CFBundleAlternateIcons下面,需要配置要设置的图标的名称(比如Icon2,Icon3,这个名称就是调用API的时候传入的图片的名称),类型为Dictionary,然后在这个名称下再配置CFBundleIconFiles和UIPrerenderedIcon,CFBundleIconFiles下配置所有尺寸的图片。


4.4 Info.plist配置小结


关于Info.plist配置这块,如果采用AppIcon.appiconset这种方式配置,那么备选图标只需要配置CFBundleAlternateIcons即可。
如果应用程序包含特定于ipad的图标版本,那么系统不会访问在CFBundleIcons配置的所有备选图标,我们需要把CFBundleIcons配置的所有备选图标复制一份到CFBundleIcons~ipad下面,结构相同。


Important: If your app contains iPad-specific versions of its icons, the system does not fall back to the alternate icons declared in the platform-agnostic version of CFBundleIcons key. Therefore, if you include any alternate icons in the CFBundleIcons key, you must include them again in your CFBundleIcons~ipad variant.

另外在指定图标名字时,最好忽略图标文件的扩展名,系统会自动选择最佳版本的图标文件。如果要指定图标文件扩展名,则所有图标都有指定文件扩展

5. API调用

说着这么半天,看一下代码吧!

struct IconItem {
    var iconName = ""
    var imageName = ""
    var displayName = ""
class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    var iconData = [IconItem(iconName: "", imageName: "Icon1", displayName: "Icon1"),
                    IconItem(iconName: "Icon2", imageName: "Icon2", displayName: "Icon2"),
                    IconItem(iconName: "Icon3", imageName: "Icon3", displayName: "Icon3")]
    override func viewDidLoad() {
        super.viewDidLoad()
        initUI()
    func initUI() {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return iconData.count
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell")
        let iconItem = iconData[indexPath.row]
        cell?.imageView?.image = UIImage(named: iconItem.imageName)
        cell?.textLabel?.text = iconItem.displayName
        cell?.textLabel?.font = UIFont.systemFont(ofSize: 30)
        return cell!
extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard UIApplication.shared.supportsAlternateIcons else {
            return
        var iconName: String? = nil
        let iconItem = iconData[indexPath.row]
        if !iconItem.iconName.isEmpty {
            iconName = iconItem.iconName
        UIApplication.shared.setAlternateIconName(iconName) { (error) in
            if error != nil {
                debugPrint("There something wrong with changing app icon!")