这里每天分享一个 iOS 的新知识,快来关注我吧
前言
前几天写了一篇关于如何无缝切换 App Logo 无需系统弹窗的技术文章,感兴趣可以先看看,这篇文章是基于上一篇的。
本篇文章以之前文章为基础,如果还没看过的可以先看之前文章。
iOS 漏洞
自从 iOS 10.3 苹果允许开发人员为应用提供替代图标以来,已经有几年时间了,开发者可以配置不同图标供用户选择,相关技术文章可以看我之前发的。
然而,这个功能还比较基础只能在用户点击时设置,而且会有一个系统的弹窗,如果不想有这个弹窗,也可以看上次发的文章,通过一些"手段"来禁止这个弹窗弹出。
目前,除了苹果自带的应用(如时钟和日历)外,没有任何动画或交互式图标------但最近,有研究人员发现了一个可以做动画的漏洞,今天就来讲讲这方面的内容。
后台执行更换图标操作
我们知道设置其他图标是通过 UIApplication.shared .setAlternateIconName
这个 API 进行的,但是这个 API 有一些限制,首先,每次执行之后,都会有一个系统弹窗,经过一些探索,上一篇文章中已经成功跳过了这个限制。
下一个限制是这个 API 无法在后台执行,如果在后台执行就会报错,经过一些探索,系统底层调用的其实是一个名为 LSApplicationProxy
的类,通过调用 LSBundleProxy
的 bundleProxyForCurrentProcess
函数来创建了一个可以设置 App Logo 的实例。那么我们为了能够调用这个私有 API,先创建出这两个类的声明:
创建 LSApplicationProxy.h
文件:
objectivec
#import <Foundation/Foundation.h>
@interface LSApplicationProxy: NSObject
- (void)setAlternateIconName:(nullable NSString *)name
withResult:(void (^_Nonnull)(BOOL success, NSError *_Nullable))result;
@end
创建 LSApplicationProxy.h
文件:
objectivec
#import <Foundation/Foundation.h>
@interface LSBundleProxy: NSObject
+ (nonnull LSApplicationProxy *)bundleProxyForCurrentProcess;
@end
然后,在你的应用中创建 LSApplicationProxy
实例:
ini
let appProxy: LSApplicationProxy = LSBundleProxy.bundleProxyForCurrentProcess()
在需要替换 App Logo 的地方执行替换方法即可:
lua
self.appProxy.setAlternateIconName(selectName) { isSuccess, error in
if let error {
print("设置 App Icon 出错: \(error)")
} else {
print("App Icon 设置成功")
}
}
执行这个方法时,你会发现前台没问题,但是退到后台之后偶尔就无法执行了,为了在后台能够成功替换,我们需要创建一个后台任务,告诉系统,我们需要在后台执行一些代码:
objectivec
UIApplication.shared.beginBackgroundTask()
这样代码放到 setAlternateIconName
之前即可。
另外,如果你的代码编译报错,提示无法找到 LSBundleProxy
,我们需要告诉编译器我们代码中有这个类,导航到 Build Settings
中,在 Other Linker Flags
中增加:
bash
-Wl,-U,_OBJC_CLASS_$_LSBundleProxy
然后运行项目,点击替换 App,然后退到后台,Logo 成功被替换:
让你的 App 图标动起来
既然能够在后台更换图标了,我们想要让图标动起来。简单的方法只需要将一个动画的每一帧做成一张 logo 图片,然后用一个定时器单位时间调用一次修改方法即可。
我这准备了几张 logo,然后 0.5 秒更换一张图片,简单演示一下:
swift
class AnimateViewController: UIViewController {
let iconNames = ["AppLogo 0", "AppLogo 1", "AppLogo 2", "AppLogo 3", "AppLogo 4"]
var index = 0
private let appProxy: LSApplicationProxy = LSBundleProxy.bundleProxyForCurrentProcess()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let button = UIButton(type: .system)
button.setTitle("开始 Logo 动画", for: .normal)
button.sizeToFit()
button.addTarget(self, action: #selector(startAnimation), for: .touchUpInside)
view.addSubview(button)
button.center = view.center
}
@objc
func startAnimation() {
UIApplication.shared.beginBackgroundTask()
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
self.appProxy.setAlternateIconName(self.getLogoName()) { isSuccess, error in
if let error {
print("设置 App Icon 出错: \(error)")
} else {
print("App Icon 设置成功")
}
}
}
}
func getLogoName() -> String {
self.index += 1
let i = index % 5
return "AppLogo \(i)"
}
}
最终效果:
动图不太清楚,可以前往我的视频号查看录屏。
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!