如何悄悄更改 iOS App icon

Mason Chang
5 min readJul 2, 2020

--

有的時候因為要做些壞壞的事、或者給客戶驚喜,我們常常會接到一個需求「希望在不更新 App 的狀態下,更換 icon」。
此篇就來介紹如何更換 App Icon,以及更進階版的更換 icon 不跳出系統彈窗。

備註: 目前這個功能只能支援 iOS 10.3 以上

更換 icon 總共分為三個步驟:

  1. 將 icon 圖檔放入 project 內
  2. 新增 icon 資料在 info.plist 內
  3. 呼叫 setAlternateIconNam
  4. method swizzling 達成悄悄更換

將 icon 圖檔放入 project 內

首先需要準備 120x120 的 2x、3x 圖檔,如果是要更換 ipad 的 icon 的話,會需要 76x76的2x、3x圖檔,接著將檔案放入專案資料夾內,如下圖。

新增 icon 資料在 info.plist 內

在你專案的 info.plist 上點選 Open As -> Source Code

新增底下這段 code (範例為兩個可替換icon)

<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>appIcon1</key> //這邊放你設定的icon name(使用系統API時會用到)
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>appIcon1</string> //這邊放你的icon檔名
</array>
</dict>
<key>appIcon2</key> //這邊放你設定的icon name(使用系統API時會用到)
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>appIcon2</string> //這邊放你的icon檔名
</array>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>

若需要替換 ipad 的 icon 的話,則需要再新增以下代碼,原理一樣。

<key>CFBundleIcons~ipad</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>ipadAppIcon1</key> //這邊放你設定的icon name
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>ipadAppIcon1</string> //這邊放你的icon檔名
</array>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string></string>
</array>
<key>UIPrerenderedIcon</key>
<false/>
</dict>
</dict>

呼叫系統的 setAlternateIconName

// 先確認是否可換 icon
guard UIApplication.shared.supportsAlternateIcons else { return }
// 設定你要替換的 icon 名稱
let iconName = "appIcon1"
// 執行 setAlternateIconName
UIApplication.shared.setAlternateIconName(name, completionHandler: { (error) in
if let error = error {
print("failed")
} else {
print("App icon changed successfully, iconName: \(name)")
}
})

備註:

呼叫 setAlternateIconName 必須要在 main thread,如果更換一直失敗的話,建議可以延遲 0.5秒

iPad 只能更換 iPad 的 icon,iPhone 只能更換 iPhone 的,圖檔不能混用

知道怎麼更換 icon 了,那如何悄悄更換呢?

由於呼叫 setAlternateIconName 這個系統 API 會跳出彈窗,如下

這個其實蠻煩人的,所以我們在這邊如果要消除這個系統彈窗,就必須使用 method swizzling 的方式。

首先先建立 UIViewController、DispatchQueue Extension

最後在 AppDelegate 的 didFinishLaunchingWithOptions 加上這段

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {// 若 alert title&message 都是 nil 則讓 alert 不出來UIViewController.runtimeReplaceAlert()return true}

但使用這個方法需要注意,如果你也有 alert 是 title、message 都是 nil 的話,你的這個 alert 也將會失效。

以上。

--

--

Mason Chang
Mason Chang

Written by Mason Chang

iOS Developer | Crypto Investor

No responses yet