【Swift, iOS10.3】アプリアイコンを動的変更できるalternateIconName

こんにちは、エンジニアの杉本です。
3月最終週、ついにiOS10.3が登場しました!

マイナーアップデートながら、レビューの返信ができるようになったりアイコン切り替えができるようになったりとかなり大きなアップデートとなりました。

今日は今回のアップデートで使えるようになったアイコン変更機能について書いてみたいと思います。

アイコン変更機能について

アイコン変更はiOS10.3から使えるようになった機能です。
下画像のようにアプリアイコンを変更する事ができます。

実装手順

この機能の実装手順は下の通りです。

事前にアイコン名をInfo.plistに登録しておき、setAlternateIconNameメソッドでそのアイコン名を指定する事でアイコンを変更できます。

  1. アイコンをプロジェクトに追加
  2. Info.plistにアイコン名を登録
  3. アイコン変更メソッドを呼び出す

事前のアイコン名の登録が必要なため、アプリ内で動的生成したUIImageなどは利用する事ができません。
アイコン数については、上限を調べようと思って200個ほど登録したところ無事に動かす事ができました。
かなりの数のアイコンを登録する事ができるようです。

アイコンの追加

まずはアイコンの登録をします。

デフォルトアイコンはAssets.xcassetsに登録します。

次に、代替アイコン(AlternateIcon1@2x.pngとAlternateIcon2@2x.png)をプロジェクトに追加します。
代替アイコンはAssets.xcassetsではうまく動かなかった為、プロジェクトに直接追加しました。

アイコン画像は FLAT ICON DESIGN様 の素材を使わせて頂きました

Info.plistにアイコン名を登録

次にInfo.plistにアイコン名を追加します。
アイコン名は下のような形式で追加します。

<key>CFBundleIcons</key>
<dict>
    <key>CFBundleAlternateIcons</key>
    <dict>
        <key>Icon1</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>AlternateIcon1</string>
            </array>
        </dict>
        <key>Icon2</key>
        <dict>
            <key>CFBundleIconFiles</key>
            <array>
                <string>AlternateIcon2</string>
            </array>
        </dict>
    </dict>
    <key>CFBundlePrimaryIcon</key>
    <dict>
        <key>CFBundleIconFiles</key>
        <array>
            <string></string>
        </array>
    </dict>
</dict>

アイコン変更メソッドを呼び出す

最後にアイコン変更処理を追加します。
UISegmentedControlを切り替える事でアイコン切り替えができる仕様にします。

まずはStoryboardにUIViewControllerとUISegmentedControlを設置します。

対応するViewControllerにアイコン変更処理を追加して、UISegmentedControlと接続します。

class ViewController: UIViewController {
    @IBAction func segmentValueChanged(_ sender: UISegmentedControl) {
        let iconName: String?
        switch sender.selectedSegmentIndex {
        case 0: iconName = nil
        case 1: iconName = "Icon1"
        case 2: iconName = "Icon2"
        default: iconName = nil
        }

        UIApplication.shared.setAlternateIconName(iconName, completionHandler: { error in print(error) })
    }
}

この状態でアプリを起動 & セグメント切り替えをすれば無事にアイコン切り替えができます。
なお、切替時に出るアラートはOS側で出しているものなので非表示にする事はできません。

デフォルトアイコンに戻したい時はsetAlternateIconNameの第一引数にnilをセットします。

UIApplication.shared.setAlternateIconName(nil, completionHandler: { error in print(error) })

登録していないアイコン名をセットした場合もデフォルトアイコンに戻ります。

UIApplication.shared.setAlternateIconName("OtherIcon", completionHandler: { error in print(error) })

切替時に出るアラートですが、ViewControllerをモーダル表示している時には表示されません。
モーダルの中でアイコン切り替えをする時は注意する必要がありそうです。

class ViewController: UIViewController {
    func changeIcon() {
        // present中はアラートが出ない
        present(UIViewController(), animated: true, completion: {
            UIApplication.shared.setAlternateIconName("Icon1", completionHandler: nil)
        })
    }
}

バックグラウンドでのアイコン変更も試したのですが、こちらはうまくいきませんでした。
実行すると下のようなエラーになります。

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func applicationDidEnterBackground(_ application: UIApplication) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
            application.setAlternateIconName("Icon1", completionHandler: { error in print(error) })
        })
    }
}

最後に

マネーフォワードではiOSの最新機能が大好きなエンジニアを募集しています。
ご応募お待ちしています。

【採用サイト】
マネーフォワード採用サイト
Wantedly | マネーフォワード

【プロダクト一覧】
自動家計簿・資産管理サービス『マネーフォワード』
Web
iPhone,iPad
Android

ビジネス向けクラウドサービス『MFクラウドシリーズ』
会計ソフト『MFクラウド会計』
確定申告ソフト『MFクラウド確定申告』
請求書管理ソフト『MFクラウド請求書』
給与計算ソフト『MFクラウド給与』
経費精算ソフト『MFクラウド経費』
入金消込ソフト『MFクラウド消込』
マイナンバー管理ソフト『MFクラウドマイナンバー』
資金調達サービス『MFクラウドファイナンス』

メディア
くらしの経済メディア『MONEY PLUS』

Pocket