近期需求中有一个需要动态替换应用图标的功能,查了下相关资料:在ios10.3之前是无法实现的,你只能提前发布拥有新应用图标的版本,来满足某个节日;节后,再发布一版,改回图标。从ios10.3开始,苹果提供了动态替换应用图标的API。至于api说明,请看苹果帮助,下面我们就按步骤实现这个功能。
1、准备好替换图标,我准备了春、夏、秋、冬四种替换图标,详见Demo。
2、在项目plist文件的根字典中添加如下:
<dict>
<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>春</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>春Icon-Small-20</string>
<string>春Icon-60</string>
<string>春Icon-Small-29</string>
<string>春Icon-Small-40</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>夏</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>夏Icon-Small-20</string>
<string>夏Icon-60</string>
<string>夏Icon-Small-29</string>
<string>夏Icon-Small-40</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>秋</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>秋Icon-Small-20</string>
<string>秋Icon-60</string>
<string>秋Icon-Small-29</string>
<string>秋Icon-Small-40</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>冬</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>冬Icon-Small-20</string>
<string>冬Icon-60</string>
<string>冬Icon-Small-29</string>
<string>冬Icon-Small-40</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
</dict>
即下图所示
/// 春天图标
@IBAction func 春Clicked(_ sender: UIButton) {
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
//当前显示的是原始图标
UIApplication.shared.setAlternateIconName("春", completionHandler: { (error: Error?) in
print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
})
}
}
}
/// 夏天图标
@IBAction func 夏Clicked(_ sender: UIButton) {
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
//当前显示的是原始图标
UIApplication.shared.setAlternateIconName("夏", completionHandler: { (error: Error?) in
print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
})
}
}
}
/// 秋天图标
@IBAction func 秋Clicked(_ sender: UIButton){
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
//当前显示的是原始图标
UIApplication.shared.setAlternateIconName("秋", completionHandler: { (error: Error?) in
print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
})
}
}
}
/// 冬天图标
@IBAction func 冬Clicked(_ sender: UIButton){
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
//当前显示的是原始图标
UIApplication.shared.setAlternateIconName("冬", completionHandler: { (error: Error?) in
print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
})
}
}
}
/// 原始图标
@IBAction func primaryClicked(_ sender: UIButton){
if #available(iOS 10.3, *) {
if UIApplication.shared.supportsAlternateIcons {
print("替换前图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
//当前显示的是原始图标
UIApplication.shared.setAlternateIconName(nil, completionHandler: { (error: Error?) in
print("替换后图标:\(UIApplication.shared.alternateIconName ?? "原始图标")")
})
}
}
}
4、运行程序,点击按钮,会出现如下alert, 你切到主屏幕界面会发现应用图标已替换。
在视图控制器中添加如下三个方法:
//利用runtime指定方法实现
func runtimeRemoveAlert() -> Void {
if let presentM = class_getInstanceMethod(type(of: self), #selector(present(_:animated:completion:))),
let presentSwizzlingM = class_getInstanceMethod(type(of: self), #selector(temporary_present(_:animated:completion:))){
method_exchangeImplementations(presentM, presentSwizzlingM)
}
}
//利用runtime恢复方法实现
func runtimeResetAlert() -> Void {
if let presentM = class_getInstanceMethod(type(of: self), #selector(present(_:animated:completion:))),
let presentSwizzlingM = class_getInstanceMethod(type(of: self), #selector(temporary_present(_:animated:completion:))){
method_exchangeImplementations(presentM, presentSwizzlingM)
}
}
//在自己实现中特殊处理
@objc dynamic func temporary_present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil){
if viewControllerToPresent.isKind(of: UIAlertController.self) {
if let alertController = viewControllerToPresent as? UIAlertController{
//通过判断title和message都为nil,得知是替换icon触发的提示。
if alertController.title == nil && alertController.message == nil {
return;
}
}
}
self.temporary_present(viewControllerToPresent, animated: flag, completion: completion)
}
6、在视图控制器中如下调用:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.runtimeRemoveAlert()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.runtimeResetAlert()
}
7、为了验证对正常的alert展示没有影响,在视图控制器中添加“alert”按钮,对于点击处理如下:
@IBAction func alertClicked(_ sender: UIButton) {
let title = "title"
let msg = "message"
let cancelTitle = "cancel"
let okTitle = "ok"
let alertController = UIAlertController(title: title, message: msg, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel) { (action: UIAlertAction) in
print("cancel")
}
let okAction = UIAlertAction(title: okTitle, style: .default) { (action: UIAlertAction) in
print("ok")
}
alertController.addAction(cancelAction)
alertController.addAction(okAction)
self.present(alertController, animated: true) {
}
}
8、运行发现替换图标时,提示已不再出现;但是聪明的你会发现,这时 completionHandler没被调用,因为他是前面展示的Alert后,点击OK按钮触发的。
工程下载地址https://github.com/qianlima210210/AlternateIconsDemo.git