美文网首页ios专题
iOS神技之动态更换APP的Icon图

iOS神技之动态更换APP的Icon图

作者: TitanCoder | 来源:发表于2018-03-16 19:56 被阅读89次
    • 在iOS10.3系统发布之前, 众所周知, 在App Store上架的APP如果要更换Icon图, 只能更新版本替换;
    • 这次苹果却在iOS10.3系统中加入了了更换应用图标的新功能,当应用安装后,开发者可以为应用提供多个应用图标选择。
    • 用户可以自由的在这些图标之间切换,并及时生效。
    • 这是因为 10.3 里引入了一个新的 API,它允许在 App 运行的时候,通过代码为 app 更换 icon

    一. 项目配置

    • 虽然提供了更换的功能,但更换的 icon 是有限制的

    • 它只能更换项目中提前添加配置好的Icon图

    • 具体可参考demo--github项目地址

    • 这里先看个效果

    更换icon.gif

    1. 备选Icon

    • 首先你需要将备选的Icon图添加到项目中,
    • 注意:
      • 图片不要放到Assets.xcassets, 而应该直接放到工程中, 不然可能导致更换Icon时, 找不到图片, 更换失败
      • info.plist 的配置中,图片的文件名应该尽量不带 @2x/@3x 后缀扩展名,而让它自动选择
    Snip20180315_1.png

    2. 配置info.plist文件

    • info.plist文件中,添加对应的CFBundleAlternateIcons的信息
    • 这里也可以查看官方的相关介绍
    Snip20180315_2.png

    Source Code添加方式如下

            <key>CFBundleAlternateIcons</key>
            <dict>
                <key>天天特价</key>
                <dict>
                    <key>CFBundleIconFiles</key>
                    <array>
                        <string>天天特价</string>
                    </array>
                    <key>UIPrerenderedIcon</key>
                    <false/>
                </dict>
                <key>小房子</key>
                <dict>
                    <key>CFBundleIconFiles</key>
                    <array>
                        <string>小房子</string>
                    </array>
                    <key>UIPrerenderedIcon</key>
                    <false/>
                </dict>
                <key>小猫</key>
                <dict>
                    <key>CFBundleIconFiles</key>
                    <array>
                        <string>小猫</string>
                    </array>
                    <key>UIPrerenderedIcon</key>
                    <false/>
                </dict>
                <key>邮件信息</key>
                <dict>
                    <key>CFBundleIconFiles</key>
                    <array>
                        <string>邮件信息</string>
                    </array>
                    <key>UIPrerenderedIcon</key>
                    <false/>
                </dict>
            </dict>
    
    
    • 注意事项:
      • 虽然文档中写着「You must declare your app's primary and alternate icons using the CFBundleIcons key of your app's Info.plist file. 」,但经测试,CFBundlePrimaryIcon 可以省略掉。在工程配置 App Icons and Launch Image - App Icons Source 中使用 asset catalog(默认配置),删除 CFBundlePrimaryIcon 的配置也是没有问题的。
      • 省略这个配置的好处是,避免处理 App icon 的尺寸。现在的工程中,大家一般都使用 asset catalog 进行 icon 的配置,而一个 icon 对应有很多尺寸的文件。省略 CFBundlePrimaryIcon 就可以沿用 Asset 中的配置。
      • 如果想设置回默认 icon,在 setAlternateIconName 中传入 nil 即可

    二. API调用

    下面我们看一下系统提供的三个API, 这里产看官方文档

    var supportsAlternateIcons: Bool
    //一个布尔值,指示是否允许应用程序更改其图标
    
    var alternateIconName: String?
    //可选图标的名称,在app的Info.plist文件中声明的CFBundleAlternateIcons中设置。
    //如果要显示应用程序的主图标alternateIconName 传nil即可,主图标使用CFBundlePrimaryIcon声明,CFBundleAlternateIcons与CFBundlePrimaryIcon两个key都是CFBundleIcons的子条目
    
    func setAlternateIconName(_ alternateIconName: String?, 
            completionHandler: ((Error?) -> Void)? = nil)
    //更改应用程序的图标
    //completionHandler: 当有结果的时候的回调
    //成功改变图标的的时候,error为nil,如果发生错误,error描述发生什么了。并且alternateIconName的值保持不变
    

    具体的实现代码:

    if #available(iOS 10.3, *) {
        //判断是否支持替换图标, false: 不支持
        guard UIApplication.shared.supportsAlternateIcons else { return }
        
        //如果支持, 替换icon
        UIApplication.shared.setAlternateIconName(imageStr) { (error) in
            if error != nil {
                print(error ?? "更换icon发生错误")
            } else {
                print("更换成功")
            }
        }
    }
    
    

    三. 消除alert弹窗

    Snip20180315_3.png
    • 动态更换App图标会有弹框, 有时候这个弹框看上去可能会很别扭, 但是这个弹框是系统直接调用弹出的, 我们又如何消除呢
    • 通过层级关系可以看到这个弹框就是一个UIAlertController, 并且是通过presentViewController:animated:completion:方法弹出的
    • 所以可以考虑使用runtime, 拦截并替换该方法, 让更换icon的时候, 不弹
    • 下面看一下具体代码:
    
    extension NoAlertChangeViewController {
        fileprivate func runtimeReplaceAlert() {
            DispatchQueue.once(token: "UIAlertController") {
                let originalSelector = #selector(present(_:animated:completion:))
                let swizzledSelector = #selector(noAlert_present(_:animated:completion:))
                
                let originalMethod = class_getInstanceMethod(NoAlertChangeViewController.self, originalSelector)
                let swizzledMethod = class_getInstanceMethod(NoAlertChangeViewController.self, swizzledSelector)
                
                //交换实现的方法
                method_exchangeImplementations(originalMethod!, swizzledMethod!)
            }
        }
        
        @objc fileprivate func noAlert_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil) {
            //判断是否是alert弹窗
            if viewControllerToPresent.isKind(of: UIAlertController.self) {
                print("title: \(String(describing: (viewControllerToPresent as? UIAlertController)?.title))")
                print("message: \(String(describing: (viewControllerToPresent as? UIAlertController)?.message))")
                
                // 换图标时的提示框的title和message都是nil,由此可特殊处理
                let alertController = viewControllerToPresent as? UIAlertController
                if alertController?.title == nil && alertController?.message == nil {
                    //是更换icon的提示
                    return
                } else {
                    //其他的弹框提示正常处理
                    noAlert_present(viewControllerToPresent, animated: flag, completion: completion)
                }
            }
            noAlert_present(viewControllerToPresent, animated: flag, completion: completion)
        }
    }
    
    
    • 这里用到了DispatchQueue.once, 这个once是我对DispatchQueue加了一个扩展
    • 在Swift4.0以后, static dispatch_once_t onceToken;这个已经不能用了
    • 关于这方面的详细介绍, 大家可以看看我的这篇文章--升级Swift4.0遇到的坑

    四. 支持不同尺寸的Icon

    • 一个标准的Icon图集, 需要十几种尺寸, 比如: 20, 29, 40, 60等
    • 对于 info.plist 中的每个 icon 配置,CFBundleIconFiles 的值是一个数组,我们可以在其中填入这十几种规格的图片名称。经测试:
      • 文件的命名没有强制的规则,可以随意取,
      • 数组中的文件名也不关心先后顺序。
    • 总之把对应的文件名填进去即可,它会自动选择合适分辨率的文件(比如在 setting 中显示 icon 时,它会找到提供的数组中分辨率为 29pt 的那个文件)。
    • 具体相关官方文档可参考, 官方介绍
    • 首先, 针对不同的尺寸, 我们要有不同的命名, 具体参考下图
    不同尺寸图片配置图
    • 文件扩展名,如@2x,@3x,要么统一不写,那么系统会自动寻找合适的尺寸。
    • 要写就需要把每张icon的扩展名写上,和上图的格式一样
    • 代码中调用图片名, 更不需要加上尺寸:
            if #available(iOS 10.3, *) {
                //判断是否支持替换图标, false: 不支持
                guard UIApplication.shared.supportsAlternateIcons else { return }
                
                //如果支持, 替换icon
                UIApplication.shared.setAlternateIconName("Sunday") { (error) in
                    //点击弹框的确认按钮后的回调
                    if error != nil {
                        print(error ?? "更换icon发生错误")
                    } else {
                        print("更换成功")
                    }
                }
            }
    
    

    相关文章

      网友评论

        本文标题:iOS神技之动态更换APP的Icon图

        本文链接:https://www.haomeiwen.com/subject/zehkqftx.html