iOS动态更换Icon的全过程记录

iOS 动态更换Icon

动态切换 App 的 icon 这个需求,在上一家公司做一款定制 App 时遇到过一次,这次领导说可能需要做,就又做了一次。虽然不是什么很难的知识点,这里也就记录一下自己做的过程吧。

  • info.plist 文件编辑
  • 更换 Icon
  • 静默切换

info.plist 文件

为了动态更换 icon,我们需要先配置一下我们项目的 info.plist 文件:

1、加入 Icon files(iOS5),其中会默认有两个 item:

  • Newsstand Icon
  • Primary Icon

2、我们需要加入我们需要的键——CFBundleAlternateIcons,类型为 Dictionary。

3、下面再添加一些字典。这里字典的键是你希望更换 Icon 的名称,在下方的 CFBundleIconFiles 数组中,写入需要更换的 Icon 的名称。

Primary Icon: 可以设置 App 的主 Icon,一般都不理会。一般主 Icon 在 Assets.xcassets 中设置。

Newsstand Icon: 这个设置一般用于在 Newsstand 中显示使用。我们也不需要理会。

这里我们就将 info.plist 编辑完成了,下面我们将对应的图片加入到项目中,这里的图片需要直接加到项目中,不能放在 Assets.xcassets 中。

更换 Icon

在 iOS 10.3,苹果开放了这个 API,可以让我们动态更换我们的 App Icon。

// If false, alternate icons are not supported for the current process.
@available(iOS 10.3, *)
open var supportsAlternateIcons: Bool { get }

// Pass `nil` to use the primary application icon. The completion handler will be invoked asynchronously on an arbitrary background queue; be sure to dispatch back to the main queue before doing any further UI work.
@available(iOS 10.3, *)
open func setAlternateIconName(_ alternateIconName: String?, completionHandler: ((Error?) -> Void)? = nil)

// If `nil`, the primary application icon is being used.
@available(iOS 10.3, *)
open var alternateIconName: String? { get }

切换到我们需要的 Icon

@IBAction func changeOneClick(_ sender: Any) {
  if UIApplication.shared.supportsAlternateIcons {
    UIApplication.shared.setAlternateIconName("lambot") { (error) in
      if error != nil {
        print("更换icon错误")
      }
    }
  }
}

这里的 iconName 直接传入项目中的 icon 名称。这里需要注意的是,项目中的名字、info.plist 中存入的名称以及这里传入的名称需要一致。

重置为原始的 Icon

@IBAction func resetClick(_ sender: Any) {
  if UIApplication.shared.supportsAlternateIcons {
    UIApplication.shared.setAlternateIconName(nil) { (error) in
      if error != nil {
        print("更换icon错误")
      }
    }
  }
}

如果需要恢复为原始的 icon,只需要在传入 iconName 的地方传入 nil 即可。

现在,已经完成了切换 Icon 的功能了。但是每次切换时,都会有一个弹框,下面我们就想办法去掉这个弹框。

静默切换

我们可以利用 Runtime 的方法来替换掉弹出提示框的方法。

以前 Method Swizzling 的时候需要在 load 或者 initialize 方法,但是在 Swift 中不能使用了。那就只能自己定义一个了。

extension UIViewController {
  public class func initializeMethod() {
    if self != UIViewController.self {
      return
    }
		// Method Swizzling
    DispatchQueue.once(token: "ChangeIcon") {
      let orignal = class_getInstanceMethod(self, #selector(UIViewController.present(_:animated:completion:)))
      let swizzling = class_getInstanceMethod(self, #selector(UIViewController.jt_present(_:animated:completion:)))

      if let old = orignal, let new = swizzling {
        method_exchangeImplementations(old, new)
      }
    }
  }

  @objc private func jt_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
    // 在这里判断是否是更换icon时的弹出框
    if viewControllerToPresent is UIAlertController {

      let alertTitle = (viewControllerToPresent as! UIAlertController).title
      let alertMessage = (viewControllerToPresent as! UIAlertController).message

      // 更换icon时的弹出框,这两个string都为nil。
      if alertTitle == nil && alertMessage == nil {
        return
      }
    }

		// 因为方法已经交换,这个地方的调用就相当于调用原先系统的 present
    self.jt_present(viewControllerToPresent, animated: flag, completion: completion)
  }
}

定义完 UIViewController 的扩展方法后,记得在 AppDelegate 中调用一下。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

  UIViewController.initializeMethod()

  return true
}

因为,Swift 中 GCD 之前的 once 函数没有了,这里自己简单定义了一个。

extension DispatchQueue {
  private static var _onceTracker = [String]()
  public class func once(token: String, block: () -> ()) {
    objc_sync_enter(self)
    defer {
      objc_sync_exit(self)
    }
    if _onceTracker.contains(token) {
      return
    }
    _onceTracker.append(token)
    block()
  }
}

defer block 里的代码会在函数 return 之前执行,无论函数是从哪个分支 return 的,还是有 throw,还是自然而然走到最后一行。

现在,我们再更换 Icon 的时候,就不会出现弹出框了。

总结

简单的知识点,时间长了不用也有可能忘记。希望自己能坚持学习,坚持记录,不断成长。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。

参考链接:

Information Property List Key Reference

(0)

相关推荐

  • iOS开发Quick Actions创建桌面Icon快捷方式

    个言 很久没发随笔了,有一年多了吧.期间也曾想继续去写随笔,但是因为各种原因而耽搁了.最近又想了一下,还是有很多东西想要写,想要分享,想要记录下来的东西.之后我也会不断写随笔,但不止于 iOS 的方向,也想去体验一下新东西.在此不多说,我们开始阅读正文吧. 简介 在支持 3D Touch 的设备上,Quick Actions 可以让用户更快,更少的操作步骤去完成他们最常做的事情,其中这么多操作可以通过主屏幕直接完成.比如用力地长按微信图标,会弹出诸如"扫一扫"和"我的二维码&

  • iOS 11 AppIcon不显示问题小结

    今天更新Xcode 9 后,在运行老项目时遇到一个小坑,就是无论如何都不显示AppIcon,在网络上找到了方法,单并没有解决,其实不是方法的问题,只是有一个小细节要注意,在这里提示一下. 出现这个问题的原因就是cocoapods与iOS 11出现点问题,这里你要更新你的cocoapods至最新版本.然后在你的Podfile文件中添加如下代码.这里一定要注意,要在end下面,如图所示 代码: post_install do |installer| copy_pods_resources_path

  • iOS动态更换Icon的全过程记录

    iOS 动态更换Icon 动态切换 App 的 icon 这个需求,在上一家公司做一款定制 App 时遇到过一次,这次领导说可能需要做,就又做了一次.虽然不是什么很难的知识点,这里也就记录一下自己做的过程吧. info.plist 文件编辑 更换 Icon 静默切换 info.plist 文件 为了动态更换 icon,我们需要先配置一下我们项目的 info.plist 文件: 1.加入 Icon files(iOS5),其中会默认有两个 item: Newsstand Icon Primary

  • 揭秘双十一手机淘宝图标如何被动态更换

    目录 1.Android如何动态更换桌面图标 1.1使用场景 1.2知识点 1.3使用Activity-alias 2.巨坑 2.1App的覆盖 2.2桌面上出现两个图标的问题 2.3桌面上图标消失的问题 2.4总结 2.5最终方案(方案一) 1.Android如何动态更换桌面图标 1.1使用场景 APP,在中国电商行业中,某宝和某东是行业的标杆.其中有一点挺让人好奇的,那就是在双十一临近之时,他们的APP桌面图标突然变成了带有双十一字样的图标.可能就是本来就内置了双十一的图标,等快到双十一的时

  • Android动态更换应用图标详情

    目录 一.背景 二.技术实现 一.背景 近日,微博官方发布了一项新功能,即可以在App设置中动态更换微博的显示图标样式.根据微博官方的说法,除了最原始的图标外,微博还推出了另外10种不同的样式,既有3D微博.炫彩微博等保留了眼睛造型的新样式,也有奶酪甜馨.巧克力等以食物命名的“新口味”,还有梦幻紫.幻想星空等抽象派新造型,给了微博用户多种选择的自由. 不过需要注意的是,这一功能并不是面对所有人开放的,只有微博年费会员才能享受.此外,iOS 10.3及以上和Android 10及以上系统版本支持该

  • JavaScript通过select动态更换图片的方法

    本文实例讲述了JavaScript通过select动态更换图片的方法.分享给大家供大家参考.具体分析如下: 下面的JS代码在select列表变化时触发SetBeerIcon()函数,SetBeerIcon()函数可以根据select选择的值动态修改图片 ... <script language="JavaScript" type="text/javascript" > function setBeerIcon() { var beerIcon = doc

  • iOS动态验证码实现代码

    具体代码如下所示: // // AuthcodeView.h // BSbracelet // // Created by Christopher on 17/5/16. // Copyright © 2017年 ZTracy. All rights reserved. // #import <UIKit/UIKit.h> @interface AuthcodeView : UIView @property (strong, nonatomic) NSArray *dataArray;//字符

  • ios动态库和静态库的区别

    一.什么是库? 库是共享程序代码的方式,一般分为静态库和动态库. 静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝. 动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存. 二.静态库和动态库的好处 使用静态库的好处: 1.模块化,分工合作 2.避免少量改动经常导致大量的重复编译连接 3.也可以重用,注意不是共享使用 动态库使用有如下好处: 1.使用动态库,可以将最终可执行文件体积缩小 2.使用动态库,多个应用程序共享内存中得

  • iOS动态调整UILabel高度的几种方法

    简介 UILabel类实现了一个只读文本视图.您可以使用这个类来画一个或多个行静态文本,比如你可能使用确定的其他部分的用户界面.UILabel类支持既简单又复杂的样式标签的文本,还可以控制外观,比如标签是否使用一个影子或吸引了一大亮点. 在iOS程序中,看的见.摸得着的,都是UIView的子类.UILabel是一个用于显示文字信息的标签视图类,即UIView的子类. 以下是关于UILabel的官方网址:https://developer.apple.com/reference/uikit/uil

  • 利用Vue Native构建移动应用的全过程记录

    目录 前言 Vue Native 的特性 声明式渲染 双向绑定 Vue.js 生态系统的丰富性 编译为 React Native 设置开发环境 创建一个Vue Native项目 Vue Native UI组件 视图组件 Image组件 TextInput组件 NativeBase UI 组件 双向数据绑定 导航和路由 状态管理 访问设备 API 总结 前言 Vue Native 是一个 JavaScript 框架,旨在使用 JavaScript 构建可以在 Android 和 iOS 上运行的跨

  • ios动态设置lbl文字标签的高度

    复制代码 代码如下: txtlbl.font = [UIFont boldSystemFontOfSize:14.0f];     txtlbl.numberOfLines = 0;  NSString *str = @"        阿方决定设立科技特网络离开电视剧分w额两个大陆高科技了了不见了日i倒计时离开我说老师肯德基弗兰克萨江东父老将费德勒说阿方决定设立科技特网络离开电视剧分w额两个大陆高科技了了不见了日i倒计时离开我立科说老师肯德基弗兰克萨江东父老将费德勒说";    CG

  • jquery动态更换设置背景图的方法

    有些时候,我们可以为用户提供很贴心的功能,比如判断用户是什么时候来访问的,然后给出一句问候,晚上好,下午好之类的.并且更换网页的背景颜色,比如晚上的时候就可以用满天星星的背景,白天就用阳光灿烂,或者特定节日就用该主题背景,让你的网站显得非常灵活,不枯燥. 下面就如何实现背景更换给出一种解决方法: 如何实现 很简单,下面是 JQuery 代码: 复制代码 代码如下: function doChangeBkg(){ var bkgUrl=$("#inputBkgUrl").val(); v

随机推荐