泽兴芝士网

一站式 IT 编程学习资源平台

「iOS开发」如何在iOS应用里实现动态更换应用图标

前言

前两天开了个百度网盘的超级会员,打开百度网盘APP之后,网盘提醒我作为尊贵的SVIP用户,要不要使用能够体现自己尊贵身份的SVIP用户专属图标,这家伙,果断确定啊,然后就发现,应用图标发生了变化。

下面两张图,图 2-1是普通用户的图标,图 2-2是尊贵的SVIP专属图标,是不是瞬间感觉自己的身份就不一样了?



看到了这个功能之后,我就着手研究实现的方法。说实话,这功能,网上一搜就是一堆文章,我研究之后进行了功能实现,然后就写了这篇笔记记录下来。

设置AppIcon

每个上架App Store的应用都必须有AppIcon,所以这个没什么可说的,我的AppIcon如下图

准备应用内切换的图标

我这边准备的是每种图标准备两个,一个两倍图,一个三倍图,尺寸分别为 120*120180*180,我准备的三个图标是 Mac_OS_Big_Sur_1Mac_OS_Big_Sur_2Mac_OS_Big_Sur_3,如下图所示:

当然,你也可以给每种图标都按照AppIcon那样配置一整套,但是要记得图片名不能重复哦。

将图标添加到项目里

在图标准备完毕后,我们将其添加到项目中,记住,放到 Assets.xcassets 里面是没有什么卵用的,我们直接自己新建文件夹,拖进去就好,如下图:

在 Info.plist 文件中添加图标

Info.plist 文件里的添加 Icon files(iOS 5) 这个key之后进行操作,这个key在源码里是 CFBundleIcons,基本结构如下所示:

{
  "Icon files(iOS 5)": {
    // 字典,我们自己添加的
		"CFBundleAlternateIcons": {
			// 字典,我们准备用来切换的图标,每个图标都是这样一个字典
			"Mac_OS_Big_Sur_1": {
        // 数组,存放我们的图标名,
        // 如果我们给每个图标准备了多个图标,比如@2x、@3x,在这里面添加就好了
				"CFBundleIconFiles": []
      }
    },
    // 默认的icon,也不用管
		"Primary Icon": {},
    // 不用管
		"Newsstand Icon": {}
	}
}

我根据自己准备的图标,做了如下图的配置

相关API

以下是Apple提供的API:

extension UIApplication {
	// 只读属性,用来判断是否可以修改图标
	// 开发的时候可以根据这个属性,决定是否显示修改图标的入口
	@available(iOS 10.3, *)
	open var supportsAlternateIcons: Bool { get }
	    
	// 修改图标的主要方法,将图标名传进去就可以修改了
	// 如果图标名传nil,则会将图标修改为基本图标,也就是使用AppIcon
	// 注意,completionHandler 这个回调是在任意的后台队列,不是主队列,
	// 要是在设置回调里修改UI之类的操作,得自己回到主队列再操作
	@available(iOS 10.3, *)
	open func setAlternateIconName(_ alternateIconName: String?, 
                                 completionHandler: ((Error?) -> Void)? = nil)
	
	// 获取当前使用的图标的名称
	// 如果拿到的是nil,表示使用的是AppIcon
	@available(iOS 10.3, *)
	open var alternateIconName: String? { get }
}

修改图标的代码

感觉就那么几个API,代码没啥可写的,随便看看API都能写出来

// 先判断能不能修改,如果能修改,直接调用系统的API进行修改
guard UIApplication.shared.supportsAlternateIcons else {
    return
}
UIApplication.shared.setAlternateIconName("Mac_OS_Big_Sur_1", 
                                          completionHandler: nil)

自动获取图标列表

可以在代码中写死图标列表,也可以直接读取 Info.plist 文件中的配置信息
作为为了避免开发的时候添加了图标还得在代码中添加的麻烦,还是直接读取简单些
我在示例代码中用的是系统方法读取
要是配合 SwiftyJSON 之类的第三方框架使用,会更简单些

// 读取 CFBundleIcons 中的值,也就是 Icon files(iOS 5)这个key对应的值
guard let bundleIcons = Bundle.main.infoDictionary?["CFBundleIcons"] as? [String: Any] else {
    return
}
// 读取AppIcon对应的值
if let bundlePrimaryIcon = bundleIcons["CFBundlePrimaryIcon"] as? [String: Any] {
    // 读取图标名称,设置的时候需要用到,不过AppIcon就用不到了,直接设置nil即可
    let iconName = bundlePrimaryIcon["CFBundleIconName"] as? String
    // 图标对应的图片列表,可以读取出来展示给用户预览
    let files = bundlePrimaryIcon["CFBundleIconFiles"] as? [String] ?? []
    print("AppIconName:\(iconName ?? "nil")")
    print("AppIconFiles:\(files)")
}
    
// 读取其他用来修改的图标
if let bundleAlternateIcons = bundleIcons["CFBundleAlternateIcons"] as? [String: Any] {
    // bundleAlternateIcons 中的每个键值对都是一个图标
    // 因为是无序的,所以每次拿到的顺序不一定一样哦
    // 要是希望顺序一样,可以根据key进行排序
    bundleAlternateIcons.forEach {
        // key:每个图标的名称,用来设置图标时使用
        print("图标名:\($0.key)")
        // value:每个图标的相关信息,包括图片列表
        if let value = $0.value as? [String: Any] {
            // 图标对应的图片列表,可以使用其中的一个作为展示图给用户预览
            let files = value["CFBundleIconFiles"] as? [String] ?? []
            print("图标\($0.key)对应的图片文件列表:\(files)")
        }
    }
}

隐藏修改之后的弹窗

修改之后系统会有一个弹窗告诉用户修改了图标,如果觉得不想要,可以把它隐藏掉

在修改完成后,系统会调用
UIApplication.shared.keyWindow?.rootViewController
func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) 方法弹出一个 UIAlertController,而且弹出来的 UIAlertControllertitlemessage 是为 nil 的,所以我们只需要对 rootViewController 的这个方法进行进行重写,然后根据 titlemessage 是否为空决定是否调用即可,示例代码如下:

override func present(_ viewControllerToPresent: UIViewController,
                      animated flag: Bool,
                      completion: (() -> Void)? = nil) {
    if let alertVc = viewControllerToPresent as? UIAlertController {
        if alertVc.title == nil && alertVc.message == nil {
            return
        }
    }
    super.present(viewControllerToPresent,
                  animated: flag,
                  completion: completion)
}

这个隐藏弹窗的方式略微有些过于暴力,而且可能会存在一些问题。

而且个人并不建议隐藏,毕竟更换图标这种事儿,还是告诉用户一下比较好。

结尾

本文是笔者通过网络查找资料等方式学习,并经过编写Demo进行验证,最终形成的一篇笔记类文章,如果文中有表述不当之处,各位可以在评论区留言交流。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言