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

株式会社ネクスト エンジニアBlog

不動産・住宅情報サイト HOME'Sを運営する株式会社ネクストのエンジニアが提供する技術ブログです。エンジニアに役立つ情報の発信や、弊社エンジニアの活動を中心にお届けします。

Use the focus, Luke :ルーク、フォー"カ"スを使え- tvOS事始め

Swift

Apple原理主義者の大坪です。未だにスターウォーズのForceと聞くと「理力」という訳語が頭に浮かぶ私はかなり年をとっています。

いきなり何を言い出したかといえば、あれですよ、新しいApple TV。素敵ですよね。日本にいながらアトランタやノースカロライナのローカルニュースを見られるなんて...(以下Apple TV礼賛が数千行続くが省略)


しかしながら


「わーいApple TVたのしいぞー」と喜んでいる場合ではない。iOSでコードを書く身であれば、その上で動くアプリケーションを作らなければならない。(断言)

Apple TVでアプリを作るには主に二つ方法があるようです。しかしマークアップランゲージが苦手で、WPFでプログラムを書くときですらXAMLを避けるような私にとって選択肢は"Traditional App"しかないでしょう。iOSと同じ「ような」フレームワークを使ってごりごり書くのです。

しかし

書き始めると上の行で「ような」が巨大になっている理由を理解することになる。似て非なるものとまではいいませんが、tvOS上でのアプリ開発はiOS上でのそれとはかなり異なっている点がある。今回はその中で一番大きなものの一つである"Force"じゃなくて"Focus"について書きます。




iOSでアプリを作っていると忘れてしまいますが「画面の任意の場所をユーザが直接操作できる」というのは偉大なことです。これはタッチパネルなしにはありえない。

ではiPhoneが登場する前はどうしていたか?ガラケー上でアプリを作っていた時のことを思い出しましょう。十字キーでもって画面上のハイライトしている部分を動かし、決定キーを押す。何かが実行される。つまりユーザが意図している場所はどこなのか?ということをハイライトで示していたわけです。

さて、あたりまえですがApple TVを接続したTVの画面をタッチしても何も起こりません。操作するためにはSiri Remoteなるリモコンを使う必要がある。そこにはTouch Surfaceがついていますが、ここで直接画面上のボタン等を指定できるわけではない。ガラケーでのハイライトに相当するものは、tvOSではフォーカスと呼ばれています。Touch surfaceをすいすいやることでフォーカス移動し、しかるのちにポンと押す。あたかもガラケー時代に先祖返りしたかのような感覚が味わえます。


つまり


tvOSでアプリを作るということは、このフォーカスと付き合うことでもある。さて、ここで問題です。Touch Surface上であれこれ操作した時フォーカスはどう移動するのでしょうか?


答え:Focus Engineの御心のままに


Focus Engineとは、tvOSでフォーカスの移動を全て司っているUIKitの中のシステムです。我々プログラマーにできることは

「ここにフォーカスを移動させてもらえませんかねえ?」

とお願いすることであり、その結果何が起こるかは Focus Engineの心次第。もちろんFocus Engineは乱数に従ってフォーカスを移動させているわけでは(多分)なく、画面上に配置された部品から「当然考えられるように」移動させるわけです。しかしAppleの開発者向けページに"Debugging Focus Issues"なる項目があるのは故のないことではない。tvOS上でアプリを作ると、ここに書いてあるデバッグ方法を何度も実行することになります。ええい、なぜフォーカスがこの部品に移動しない!と呪いの言葉を吐きながら。


というわけでtvOS上でのアプリ開発第一歩として、「とにかくフォーカス移動させてみる」というのをやります。サンプルプログラムをGithubに置きました。機能はほとんどないのですけど、最初の一歩はこういうシンプルなものがいいですよね?ね?ね?(血走った目で懇願)

ダウンロードして実行してみましょう。Apple TVのアプリといえば、画面の上からにょろっと出てきたり引っ込んだりするタブバー。というわけで無駄にUITabBarControllerを使ったUIViewControllerの切り替えも実装しています。

f:id:nextdeveloper:20151221162157g:plain

さてここで問題です。このタブバーの出し入れはどうやっているでしょう?


答え:Focus Engineの御心のままに


細かいことは省略しますが、とにかくコードの方では何もやっていません。タブバーにフォーカスが移動すればバーがにょろっと出る。Viewにフォーカスが移動すれば引っ込む。わーい自分で何もしなくても動いてくれてうれしいな、と言っていられる期間はそう長くないかもしれません。

さて、次の問題です。First Tabと書かれたタブの項目を選ぶと赤い画面が表示され、Firstと書かれたボタンにフォーカスが当たる。位置関係からして「まあ当然だな」と思うわけですが、場合によってはSecondと書かれたボタンに最初にフォーカスが当たって欲しいこともあるでしょう。どうすればそれができるか?


答え:Focus Engineにお願いする


大事なことなので2度書きますが、ここでできるのは「お願いする」ことであり「命令する」ことではない。その結果何が起こるかを決定するのはFocus Engineなのですが、とにかく「お願い」してみましょう。FirstViewController.swiftをみると16行目からこんな部分があります。

//    override weak var preferredFocusedView: UIView? {
//        return secondButton
//    }

わざとらしいコメントアウトを外し、再度実行するとこんな風に動きます。(First Tabという項目上でタップしています)

f:id:nextdeveloper:20151221161917g:plain

タブバーが引っ込んだ時、Secondと書かれたボタンにフォーカスがあたっています。 ここで何が起こっているか?Focus EngineがFirstViewControllerにpreferredFocusedView(フォーカスを当てて欲しいView)は?と聞き、それに対してsecondButtonと答えているわけです。これによりめでたくタブから"Second"と書かれたボタンにフォーカスが移動する。


ちなみに、ここでFirst Tabにフォーカスがあたった状態から下にスワイプすると、前と同じようにタブバーが消え、Firstボタンにフォーカスが移ります。なんだこれは?と問うてはいけません。*1これは仕様です。First Tab上でタップされた時は「そもそもどっちの方向に行こうとしているかわからない」のでpreferredFocusedViewを尋ねてくれるわけですが、スワイプだと「ああ、下に行きたいわけね」とそのすぐ下にあるViewにフォーカスを移す。preferredFocusedViewに何を書いておこうと、聞く耳持たないわけです。いや、ちょっと待て、それは困る、というのならばFirstボタン自体がフォーカスを受け取れないようにしておいて、一旦Secondボタンにフォーカスが移った後にフォーカス可能にする...とかやりだす。ふと気がつけば足元にフォー"カ"スの暗黒面が大きな口を開けていることに気がつく。


かくのごとく


tvOS上でアプリを作ろうとすれば、どこかでFocusの使い方-お願いの仕方-と向き合う必要がある。Star Wars Episode Vで、フォースを身につけるためルークはヨーダを背負ってあちこち走り回っていました。同じような厳しいトレーニングをするのは自由ですが、そうしてもApple TVのフォーカスを思うように動かすことはできない。

素直にボタンを格子状に並べておけばフォーカスについて悩むこともないのかもしれませんが、それでは面白くない。ではtvOSでの「面白い」とはなんなのか。iOSのアプリとどう違うべきであるのか、については以下次号(続くのか?)

*1:ちなみに私はここで丸一日つぶしました。