今回よりiOS8の機能の中から注目すべきものをピックアップし、その中身についてより深く追ってみたいと思います。
本記事では新機能「Extension」の1つ、Today Extensionについて紹介します。
Today Extensionは、一言で言うとウィジェットです。 弊社アプリ「ファミキャプ」にToday Extensionの機能を実装しましたので、その実装過程について紹介していきたいと思います!
ファミキャプでは過去に発売されたゲームの発売日が今日と同じ日付だった場合に、ゲームタイトルを表示するウィジェットを作成してみました。
完成したウィジェットは以下のようになります。
※以降は、既にcontaining app(ホストになるアプリ、本記事ではファミキャプ)がある前提で説明していきます。
Today Extensionの設定
まず最初にToday Extensionの設定を行います。
- XcodeのメニューバーのEditor > Add Targetを選択します。
- Today Extensionを選択してNextボタンをクリックします。
すると以下のような画面が表示されます。- Product Name → Today Extension用のアプリのプロダクト名を入力。(ここでは「SaleDate」と入力しました)
- 入力したらFinishボタンを押下。
- Product Name → Today Extension用のアプリのプロダクト名を入力。(ここでは「SaleDate」と入力しました)
以下のようなアラートが表示されたら、Activateボタンをクリックします。
プロジェクトツリーを見ると「SaleDate」ディレクトリが作成され、その下にいくつかファイルが作成されました。
MainInterface.storyboardは、Today Extensionで表示される画面を構成するためのファイルとなります。
画面に対して何か処理を行う必要がある場合はTodayViewControllerに対して処理を記述していく必要があります。
ひとまずこの状態でアプリをビルドして起動してみます。
ビルドすると上記のような選択画面が表示されます。
今回Today Extensionでの起動なのでTodayの箇所にフォーカスが当たっている事を確認したらRunボタンを押下します。
端末側で通知センターを表示させると以下のように表示されているのが確認できるかと思います。
表示されていない場合は、通知センターの下端にある「編集」ボタンを押下すると下の画面になりますので、ここからアプリの隣の「+」ボタンを押下してください。
しかしこのままでは世に公開できるものではないので、ここからカスタマイズしていきたいと思います。
ヘッダーのタイトルの設定
まずはヘッダーのタイトルの表示を変更してみます。
タイトル部分とはアプリのアイコンの右隣に表示されている文字になります。(上図では「SaleDate」と表示されている箇所です)
タイトルを変更するには、プロジェクトツリーに新しく作成されたSaleDateディレクトリ内のSupporting Filesディレクトリ内のInfo.plistを開きます。
Bundle display nameという箇所の部分がヘッダーに表示されるタイトルになるので、ここをアプリ名と同じ「ファミキャプ」に変更します。
※ 再ビルドしてインストールしてみてもヘッダーのタイトルの表示が変わらなかったので、もし変わらなかった場合は一度アプリをアンインストールしてから試してみるとよいです。
ビルドして確認するとタイトルの表示が変わる事を確認できるかと思います。
レイアウトの作成
ヘッダーのタイトルの設定が完了したので次はレイアウトの作成を行います。
レイアウトファイルは前述したようにデフォルトではMainInterface.storyboardとなっているので、このファイルを編集していきましょう。
レイアウトの作成については詳細は記載しませんが、ドキュメントを見るといくつか注意点があります。
- Auto Layoutを活用する
- ウィジェット内にスクロールビューを置かない ウィジェット画面は縦にも横にもスクロールができるため、ウィジェット本体のビューをスクロールするように作成してしまうと動作が困難になってしまう危険性があります。
コードでの編集
MainInterface.storyboardで作成されたビューコントローラーはTodayViewController.mで処理の記述が可能となります。
テンプレートで作成されたTodayViewControllerクラスはNCWidgetProvidingプロトコルを継承しており、このプロトコルがウィジェットの作成に必要な処理を提供しています。
以下で主な機能について紹介したいと思います。
ウィジェットの更新処理
ウィジェットの更新処理はNCWidgetProvidingプロトコルが提供する(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandlerメソッドによって処理を記述します。
このメソッドではデフォルトで以下のような記述になっているかと思います。
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler { // Perform any setup necessary in order to update the view. // If an error is encountered, use NCUpdateResultFailed // If there's no update required, use NCUpdateResultNoData // If there's an update, use NCUpdateResultNewData completionHandler(NCUpdateResultNewData); }
ウィジェットの更新処理を記述して最後にcompletionHandlerメソッドを呼びだします。
コメントに記載があるようにこのメソッドの引数には3タイプ指定が可能となっています。
- NCUpdateResultFailed
- 更新処理中にエラーが発生
- NCUpdateResultNoData
- 更新は不要
- NCUpdateResultNewData
- 内容が入れ替わったのでビューの再描画が必要
更新処理の結果に応じて、上記の引数を設定するようにしてください。
ここで公式のドキュメントにも記載がありますが、注意点として以下が挙げられます。
- 何段階かに分けて実行するタスクや、アップロード/ダウンロードなど長時間を要するタスクには向いていません。
このメソッド内では上記の注意点に気をつけて実装してください。
ウィジェットのマージンの設定
ウィジェットにはマージンが設定できます。
何も処理を記述しないとデフォルトでシステムが設定したマージンに設定されるはずです。
マージンを変更したい場合は、NCWidgetProvidingプロトコルが提供する(UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsetsメソッドを使用してマージンの設定を行います。
今回ファミキャプでは以下のようなマージンに設定してみました。
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets { // ウィジェットのマージンを設定. return UIEdgeInsetsMake(0, defaultMarginInsets.left, 0, 0); }
引数で渡されるdefaultMarginInsetsはシステムが設定しているデフォルトのマージンの値となります。
上記コードは左側だけマージンを設定して、他の部分(上、右、下)は0に設定しています。
App Groupsについて
今回ファミキャプのウィジェットで実装したい項目として、ファミキャプ本体アプリでキャプチャし保存された画像をウィジェット側で表示したかったのですが、本体アプリとウィジェット側のアプリとで保存データのアクセスを行うにはApp Groupsという機能を使用しなければなりませんでした。
このApp GroupsというのはiOS8の新機能で、同一ディベロッパーがリリースしたアプリ間でデータのストレージを共有できる機能です。
このストレージには任意のファイルの書き込みが可能で、UserDefaultsや画像データなどを格納して共有する事が可能です。
次回は、引き続きファミキャプへの実装を例に、App Groupsの使い方について述べいたいと思います。
参考URL
App Extension Programming Guide(英語版)
App Extensions プログラミングガイド(日本語版)
iOS Human Interface Guidelines