読者です 読者をやめる 読者になる 読者になる

実装してわかった!iOSとはここが違うねtvOS!- その1

f:id:cyberz-dev-writer:20160315200214j:plain 参考画像:How to update your Apple TV software - iDownloadBlogより

はじめに

どうも。OPENREC事業部でゲーム実況動画共有プラットフォームサービスOPENREC.tviOSエンジニアをしている辰己です。
OPENREC.tvでは、有名実況主さんの実況動画が毎日たくさんアップされて盛り上がっていますので、ぜひAppStoreGooglePlayからダウンロードしてみてくださいね!

さて、tvOSアプリを実装する前に確認すべき5つのことについてまとめた前回に引き続き、今回は実際に実装してみてわかったtvOSのアプリ開発はiOSと比べてここが違う!と感じたことについてまとめていこうと思います。

フォーカスの概念が重要

tvOSのアプリ開発ではiOSでも使用できたジェスチャーであるタップ、スワイプ、パンに加えて、フォーカスという概念がとても重要な特徴の一つとなっています。
AppleTV第四世代の新しいリモコンであるSiri RemoteではTouchサーフェスによるトラックパッドのようなカーソル移動がメインになっているため、いまどこにカーソルがいるのかを判断するためにフォーカスの概念が導入されています。
なので、UIを設計する際は各UIパーツがフォーカスを検知できるのかどうかを知っておく必要があります。
現在、フォーカスを検知できるUIパーツは以下の7種類です。

  • UITabBar
  • UIButton
  • UITableViewCell
  • UICollectionViewCell
  • UISegmentedControl
  • UITextField
  • UISearchBar

また、フォーカスが当たっているビュー上に設置した UIImageView オブジェクトはadjustsImageWhenAncestorFocusedプロパティをYESにすると、フォーカスが当たった際にサイズが自動的に大きくなり、スワイプに応じたパララックスの動きが付与されます。
フォーカスが当たらないビューには非常に便利なプロパティなのですが、指定できるのはUIImageViewだけです。

UIImageView *thumbnailImageView = [[UIImageView alloc] init];
thumbnailImageView.adjustsImageWhenAncestorFocused = YES;

f:id:cyberz-dev-writer:20160315203358g:plain:w500
参考画像: tvOS - Icon Parallax Effectより

続いて、各ビューにフォーカスが当たったときに呼ばれるのがUIFocusEnvironmentプロトコル- (void)didUpdateFocusInContext:withAnimationCoordinator:メソッドです。
UIFocusEnvironmentは UIView クラスや UIViewController クラスで定義されているため、そのサブクラスであれば、このメソッドフォーカスの移動を検知できます。

tvOSではapplicationFrameは使えない

iOSでは、デバイスに応じたスクリーンサイズを取得する際に、UIScreenクラスから [UIScreen mainScreen].bounds と [UIScreen mainScreen].applicationFrame という2つのプロパティが使えましたが、tvOSでは後者の applicationFrame が使えなくなりました
なぜ使えなくなったのかを考えてみると、以下の3つの理由が思い当たりました。

  1. AppleTVにはステータスバーの概念が無いから
  2. AppleTVにはマルチタスキングのようなスプリットビューの概念が無いから
  3. テレビは回転を検知できないから

1つ目に、AppleTVではiPhoneで常に表示されていたはずのステータスバー(画面の一番上にある電池残量や現在時刻などが表示されている部分)が表示されなくなったからです
[UIScreen mainScreen].bounds は画面そのもののフレームサイズを取得するときに使用しますが、[UIScreen mainScreen].applicationFrameは主にステータスバーを取り除いた画面のフレームサイズを取得する際に使用するため、ステータスバーがなくなったAppleTVでは、使う必要がありませんね。
2つ目に、iOS9からiPadに実装された新機能であるマルチタスキングのようなスプリットビューの概念がtvOSにはないからです。
applicationFrame はマルチタスキング時のアプリのフレームサイズを取得するときにはとても頼りになるプロパティだったのですが、その概念がないので使う必要がありません。
そして、3つ目に、テレビは回転を検知できないからです。
まあ、アプリの使用中にタブレットみたいにテレビを回転させて使うなんてことは普通ありませんよね。
つまり、スクリーンのフレームサイズが1パターン(ランドスケープで全画面)しかないので、applicationFrameを使う必要がなくなるのです。

ボタンのクリック検知に UIControlEventTouchUpInside(ボタンを押し込んで指をはなしたタイミングを検知する)を指定したのに反応しない

iOSアプリの開発ではボタンを押したときのアクションを実装するときに、よく以下のようなコードで実装します。

[UIButtonオブジェクト addTarget:self
                    action:@selector(呼び出したいメソッド)
          forControlEvents:UIControlEventTouchUpInside];

AppleTVのリモコンにも押し込んでボタンを決定する操作があるし、同じコードで大丈夫だろうと思って実装したら、、、あれ?、、、何回クリックしてもメソッドが呼ばれないではありませんか!ナンテコッタイ!他にも UIControlEventTouchDown(ボタンを押し込んだタイミングを検知する)も使ってみましたが、反応なし。
そこで、UIKitの中身を覗いてみると、forControlEventsのオプション指定に新たな項目が増えていました。それが UIControlEventPrimaryActionTriggeredというより長い名前になったオプションです。
英語の意味から察するに、そのクラスの主なアクションイベントを検知するためのオプションのようですね。あらゆるアクションを包括的に検知できるということかな?と考え、これを使ってみると、一発で反応しました!
なので、tvOSでボタンを押したときのアクションを実装するには以下のようにしましょう。

[UIButtonオブジェクト addTarget:self
                    action:@selector(呼び出したいメソッド)
          forControlEvents:UIControlEventPrimaryActionTriggered];

最後に

本日はここまでです。
今回の内容は違うタイトルで社内勉強会でも発表させていただきました。

次回はもっとソースコードを本格的に使用した実装編を予定していますので、お楽しみに。
今回も最後まで読んでいただきありがとうございました。