iOS 10.3 提供了动态变更icon的API;
这里整理了一些注意事项和使用过程中的问题;
注意事项:
1、icon添加的位置;
不在Assets.xcassets中,在项目下创建文件夹添加各icon,
image.png如图,如果添加了两套icon,2020和spring,则在相应文件夹下添加不同尺寸的图片;
image.png
2、info.plist 添加方式;
建议文本编辑器打开项目info.plist,然后直接贴上如下代码,替换中间的icon的key和对应的图片数组;
<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>2020</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>2020_60X60</string>
<string>2020_29X29</string>
<string>2020_40X40</string>
<string>2020_58X58</string>
<string>2020_76X76</string>
<string>2020_80X80</string>
<string>2020_87X87</string>
<string>2020_120X120</string>
<string>2020_152X152</string>
<string>2020_167X167</string>
<string>2020_180X180</string>
<string>2020_1024X1024</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>normal</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>normal_60X60</string>
<string>normal_20X20</string>
<string>normal_40X40</string>
<string>normal_29X29</string>
<string>normal_58X58</string>
<string>normal_60X60</string>
<string>normal_76X76</string>
<string>normal_80X80</string>
<string>normal_87X87</string>
<string>normal_120X120</string>
<string>normal_152X152</string>
<string>normal_167X167</string>
<string>normal_180X180</string>
<string>normal_1024X1024</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
<key>spring</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>spring_60X60</string>
<string>spring_29X29</string>
<string>spring_58X58</string>
<string>spring_76X76</string>
<string>spring_80X80</string>
<string>spring_87X87</string>
<string>spring_120X120</string>
<string>spring_152X152</string>
<string>spring_167X167</string>
<string>spring_180X180</string>
<string>spring_1024X1024</string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>normal</string>
</array>
</dict>
</dict>
默认使用的过程中会有如下问题:
1、如果在viewDidLoad/viewDidAppear 方法中使用,提示:“Error Domain=NSCocoaErrorDomain Code=3072 操作已取消”
延迟1秒钟操作,附上更换的代码
override func viewDidLoad() {
super.viewDidLoad()
delay(0.1) { [weak self]() in
guard let `self` = self else {return}
self.changeIcons()
}
}
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
func changeIcons() {
if #available(iOS 10.3, *) {
//判断是否支持替换图标, false: 不支持
guard UIApplication.shared.supportsAlternateIcons else { return }
//如果支持, 替换icon
DispatchQueue.main.async {
UIViewController.runtimeReplaceAlert()
}
//name即为info.plist中添加的各个key值
UIApplication.shared.setAlternateIconName("2020", completionHandler: nil)
}
}
2、默认使用API,会有默认UIAlertController提示;如上代码,可能发现出现了一段UIViewController.runtimeReplaceAlert()的方法调用;根据弹出框无title,无message来替换系统的present方法,但是这里也要注意一点,替换完使用完,记得恢复默认,不然app中其它UIAlertController的弹出会有问题;
extension UIViewController {
public class func runtimeReplaceAlert() {
DispatchQueue.once(token: "UIAlertController") {
let originalSelector = #selector(self.present(_:animated:completion:))
let swizzledSelector = #selector(self.noAlert_present(_:animated:completion:))
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
if didAddMethod{
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
}else{
if originalMethod != nil {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
}
}
public class func runtimeResetAlert() {
DispatchQueue.once(token: "UIResetAlertController") {
let swizzledSelector = #selector(self.present(_:animated:completion:))
let originalSelector = #selector(self.noAlert_present(_:animated:completion:))
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
if didAddMethod{
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
}else{
if originalMethod != nil {
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 {
//更换完恢复系统默认present方法
DispatchQueue.main.async {
UIViewController.runtimeResetAlert()
}
return
} else {
//其他的弹框提示正常处理
noAlert_present(viewControllerToPresent, animated: flag, completion: completion)
}
}
noAlert_present(viewControllerToPresent, animated: flag, completion: completion)
}
}
网友评论