阅读 647

Swift:小技巧——更换App应用图标

这是我参与更文挑战的第14天,活动详情查看: 更文挑战

这个功能你们发现了吗?

不知道有没有在苹果手机上使用GitHub官方App的朋友们,你们有没有发现这样一个功能:

在这个App的设置里面,可以更换App的应用图标。

另外有个叫Cuto的壁纸App也有这个功能。(吐槽:Cuto需要是Vip用户才能使用这个功能,其实真的是很普通的代码很普通的Api呀)。

RPReplay_Final1623400225.2021-06-11 16_32_19.gif

看到这个功能的时候,我当时就震惊了,这是用的哪门子hook技术?

然后感到兴趣的我屁颠屁颠的跑去查文档一看:iOS 10.3 加入了了更换应用图标的新功能,开发者可以为应用提供多个应用图标选择。

害!原来都是系统Api可以操作了,而且还是在iOS10.3之后,只能怪自己少见多怪。话不多说,撸起代码原地走起!

配置与使用

    1. 首先选取好图片素材,切好图片,重命名。

image.png

    1. 在info.plist文件中进行配置。

plist文件配置,添加Icon files (iOS 5)类型为Dictionary。

image.png

image.png

注意: 点开Icon files (iOS 5),里面有Primary Icon,Newsstand Icon两个key,Primary Icon 里面的图片为AppIcon默认图片。不用去管这个地方。

Icon files (iOS 5)里面创建一个CFBundleAlternateIcons,类型为Dictionary。

这个地方配置起来比较复杂,我直接上info.plist中的xml便于理解,直接将以下代码CV到info.plist中的源码中即可。

<key>LSApplicationCategoryType</key>
	<string></string>
	<key>CFBundleIcons</key>
	<dict>
		<key>CFBundlePrimaryIcon</key>
		<dict>
			<key>CFBundleIconFiles</key>
			<array>
				<string></string>
			</array>
		</dict>
		<key>CFBundleAlternateIcons</key>
		<dict>
			<key>blackDuck</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>blackDuck-20</string>
					<string>blackDuck-29</string>
					<string>blackDuck-40</string>
					<string>blackDuck-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>blackSeele</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>blackSeele-20</string>
					<string>blackSeele-29</string>
					<string>blackSeele-40</string>
					<string>blackSeele-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>flowerWeddingRita</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>flowerWeddingRita-20</string>
					<string>flowerWeddingRita-29</string>
					<string>flowerWeddingRita-40</string>
					<string>flowerWeddingRita-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>FlowerWeddingSakura</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>FlowerWeddingSakura-20</string>
					<string>FlowerWeddingSakura-29</string>
					<string>FlowerWeddingSakura-40</string>
					<string>FlowerWeddingSakura-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>fuHua</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>fuHua-20</string>
					<string>fuHua-29</string>
					<string>fuHua-40</string>
					<string>fuHua-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>grandmother</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>grandmother-20</string>
					<string>grandmother-29</string>
					<string>grandmother-40</string>
					<string>grandmother-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>himeko</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>himeko-20</string>
					<string>himeko-29</string>
					<string>himeko-40</string>
					<string>himeko-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>iceDuck</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>iceDuck-20</string>
					<string>iceDuck-29</string>
					<string>iceDuck-40</string>
					<string>iceDuck-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
			<key>whiteSeele</key>
			<dict>
				<key>CFBundleIconFiles</key>
				<array>
					<string>whiteSeele-20</string>
					<string>whiteSeele-29</string>
					<string>whiteSeele-40</string>
					<string>whiteSeele-60</string>
				</array>
				<key>UIPrerenderedIcon</key>
				<false/>
			</dict>
		</dict>
	</dict>
复制代码
    1. 这里我通过tableView点击进行AppIcon切换,逻辑简单,直接上代码了。
import UIKit

class ViewController: UIViewController {
    
    let sizeString = "-60"
    
    lazy var dataSource: [(String, String)] = [(title: "姬子", imageName: "himeko"),
                                               (title: "白希", imageName: "whiteSeele"),
                                               (title: "花嫁丽塔", imageName: "flowerWeddingRita"),
                                               (title: "赤鸢仙人", imageName: "fuHua"),
                                               (title: "黑希", imageName: "blackSeele"),
                                               (title: "黑鸭", imageName: "blackDuck"),
                                               (title: "花嫁桜", imageName: "FlowerWeddingSakura"),
                                               (title: "理律", imageName: "iceDuck"),
                                               (title: "岳母", imageName: "grandmother"),
    ]
    
    private lazy var tableView: UITableView = {
        let tableView = UITableView(frame: view.frame, style: .plain)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
        tableView.tableFooterView = UIView(frame: CGRect.zero)
        tableView.rowHeight = 66
        return tableView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setUpUI()
    }
    
    private func setUpUI() {
        view.backgroundColor = .white
        title = "更换AppIcon"
        view.addSubview(tableView)
    }
    
    
    @objc
    private func changeAppIcon(iconName: String) {
        let app = UIApplication.shared
        if app.supportsAlternateIcons {
            app.setAlternateIconName(iconName, completionHandler: { (error) in
                if error != nil {
                    print("error => \(String(describing: error?.localizedDescription))")
                }else {
                    print("Done")
                }
            })
        }
    }

}

extension ViewController: UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSource.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)
        cell.textLabel?.text = dataSource[indexPath.row].0
        cell.imageView?.image = UIImage(named: dataSource[indexPath.row].1 + sizeString)
        return cell
    }
}

extension ViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: false)
        let iconName = dataSource[indexPath.row].1
        changeAppIcon(iconName: iconName)
    }
}


复制代码

其核心Api是UIApplication.shared的以下方法:

// 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)
复制代码

好了,说实话我搞切图和重命名都比写代码累!!!

最后来一张效果图

IMG_6984.GIF

你Get√到这个技巧了吗?是不是可以一顿操作猛如虎,隔壁就眼馋了?

Github资源地址:

考虑到这个地方的info.plist文件的配置比较复杂,目前正在整理代码,完成后会附上工程链接,请大家稍安勿躁。

思考:为何很多App不没有集成更换App图标的功能呢?🤔

简单的配置,简单的Api,编写起来基本不存在任何技术难点,不过你可以看见市场上的App很少集成这个功能?

App应用图标更新的勤快如淘宝,也不过是在618、双11改改底色与副标题,也少会去动那个字,从产品的角度看,App应用图标,是用户点击的入口,需要有一定的辨识度与品牌效应。

比如你使用某个App,觉得它特别好,想推荐给身边的人,一个设计鲜明、变更不大的App图标才是最关键,反之,如果一个App图标都经常更换,又有谁记住它呢?

这只是我的一点思考,大伙觉得呢?

明日继续

端午节最后一天,大家都好好休息了吗?明天就要上班啦,大家加油!

另外,我不当舰长好多年!

文章分类
iOS
文章标签