この投稿はWatchKit Advent Calendar 2014の10日目の記事です。

Custom Notification Interfaces

Apple Watchのnotificationの画面は2種類あります。
詳しくは以下参照。
Apple WatchでのNotification受け取りについて | blog.haranicle.net

Watch Appのターゲットを作るときにNotificationにチェックを入れておくと、Storyboardに以下のようなものが出来上がります。

notification_static_dynamic

Dynamicの方はOptionalなので削除することもできます。
また、削除したあとに、以下の手順で手動で作ることもできます。

  • Interface ControllerをStoryboardを配置し、Staticから配置した Interface ControllerにSegueを伸ばし、”dynamic controller interface”をクリック
  • または、後述のカテゴリ編集画面のチェックボックスで追加することもできます

NotificationはGlanceと異なり、1つのWatch Appで複数持つことができます。

Notificationが届くと、WatchKitはDynamicのInterfaceを表示しようとします。
あなたがDynamicのInterfaceを提供しなかった場合、または何らかの理由でDynamicのInterfaceが利用できない場合は、Staticのinterfaceを表示します。
また、あなたがStaticのInterfaceを表示するように明示することもできます。
DynamicのInterfaceを構成するためには以下のメソッドを呼び出す必要があります。

  • didReceiveRemoteNotification:withCompletion:
  • didReceiveLocalNotification:withCompletion:

これらのメソッドが”WKUserNotificationInterfaceTypeDefault”を返すと、明示的にStaticのInterfaceを表示することができます。

Configuring the Notification Type of a Custom Interface

各NotificationのInterfaceは、Apple Watchにいつ使用するかを教えるために、割り当てられたNotificationタイプを持っている必要があります。
Notificationはカテゴリの値をPayloadに含むことができます。
もし、Notificationがカテゴリの値を含んでいない場合は、デフォルトのNotificationの画面を表示します。

届いたNotificationとInterfaceを対応付けるために、それぞれに同じカテゴリの値を設定する必要があります。
Interfaceのカテゴリの値はStoryboardで設定できます。
Nameの値は一意である必要があります。

notification_type_config

リモートNotificationを生成するときに、Payload内にカテゴリの値を含めます。
カテゴリの値はStoryboardで指定したものと同じである必要があります。

Configuring the Static Notification Interface

StaticなNotificationのInterfaceを作る上でのルールは以下です。

  • すべての画像はWatch Appのバンドルに含めなければいけません。
  • Interfaceに操作できる何か、テーブル、地図を含めてはいけません。
  • InterfaceのnotificationAlertLabel outletはLabelに接続されていなければいけません。そのLabelのテキストはNotificationのメッセージがセットされます。その他のラベルの文字はStoryboardで設定した値しか表示できません。(!?)

StaticなNotificationのLabelと画像は変更できません。(だからStaticなのか!)

notification_design

Configuring a Dynamic Notification Interface

DynamicなNotificationのInterfaceを実装するためにはWKUserNotificationInterfaceControllerのサブクラスを作成する必要があります。

Designing Your Dynamic Interface

WKUserNotificationInterfaceControllerのサブクラスで実行時にLabel、画像、その他のオブジェクトをOutlet経由で変更することができます。
Notificationをタップすると、Watch Appが起動するので、インタラクティブなコントロールを含むべきではありません。

  • 基本的にLabel、画像、グループ、セパレータを使用してください。
  • テーブルや地図を含むこともできます。
  • ボタンやスイッチ、その他のインタラクティブなコントロールを含めないでください。

Configuring Your Dynamic Interface at Runtime

初期化後、WatchKitはあなたのNotificationInterfaceControllerの以下のメソッドにPayloadのデータを渡します。
このメソッド内で渡されたデータに対応するようにInterfaceを書き換えます。

  • Remote Notificationの場合 didReceiveRemoteNotification:withCompletion:
  • Local Notificationの場合 didReceiveLocalNotification:withCompletion:

このメソッドでInterfaceの書き換えがおわったら、WatchKitにInterfaceの準備が完了したことを伝えるために、Completionハンドラブロックを実行する必要があります。

notification_event_cycle_2x

didReceiveRemoteNotification:withCompletion:の実装例です。

- (void)didReceiveRemoteNotification:(NSDictionary *)remoteNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler {
    // Get the aps dictionary from the payload.
    NSDictionary* apsDict = [remoteNotification objectForKey:apsKeyString];
    // Retrieve the title of the invitation.
    NSString* titleString = [apsDict objectForKey:titleKeyString];
    [self.titleLabel setText:titleString];
    // Extract the date and time from the custom section of the payload.
    // The date/time information is stored as the number of seconds since 1970.
    NSDictionary* customDataDict = [remoteNotification objectForKey:customDataKey];
    NSNumber* dateValue = [customDataDict objectForKey:invitationDateKey];
    NSDate* inviteDate = [NSDate dateWithTimeIntervalSince1970:[dateValue doubleValue]];
    // Format the date and time strings.
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    // Call a custom method to get the localized date format string for the user.
    // The default date format string is "EEE, MMM d".
    dateFormatter.dateFormat = [self dateFormatForCurrentUser];
    NSString *formattedDateString = [dateFormatter stringFromDate:inviteDate];
    // Call a custom method to get the localized time format string for the user.
    // The default time format string is "h:mm a".
    dateFormatter.dateFormat = [self timeFormatForCurrentUser];
    NSString *formattedTimeString = [dateFormatter stringFromDate:inviteDate];
    // Set the date and time in the corresponding labels.
    [self.dateLabel setText:formattedDateString];
    [self.timeLabel setText:formattedTimeString];
    // Set the location of the meeting.
    NSString* locationString = [customDataDict objectForKey:invitationLocationKey];
    [self.locationLabel setText:locationString];
    // Set the invitation's notes (if any).
    NSString* notesString = [customDataDict objectForKey:invitationNotesKey];
    [self.notesLabel setText:notesString];
    // Tell WatchKit to display the custom interface.
    completionHandler(WKUserNotificationInterfaceTypeCustom);
}

Testing Your Custom Interface

シミュレータでDynamicなNotificationのInterfaceをテストするには、ビルドスキームを作成します。
そして、Xcodeのテンプレートで提供されたRemoteNotificationPayload.jsonをPayloadのデータとして使用します。
詳細については以下を参照してください。
WatchKit Programming Guide: Configuring Your Xcode Project

参考

WatchKit Programming Guide: Custom Notification Interfaces

宣伝

Swiftでカスタムキーボードアプリ作りました。買ってね!
特殊文字キーボード