Apple TV の UI はフォーカスモデルとなっています。画面内のどれかの要素がフォーカスを持っていて、ユーザーがリモコンなどで行うアクション(タップとか)はその要素に対して行われます。
他の要素に対してアクションしたい場合、フォンアプリでは、直接画面上で他の要素をタップすることができますが、Apple TVアプリではスワイプ操作などでフォーカスを対象の要素に移動させてからアクションする必要があります。
例)Button A にフォーカスがあるとき、Button Bをタップしたい場合
このように、Apple TV アプリではフォーカスの概念が重要になります。
フォーカスの移動先はどのように決まるのか
基本的には、画面上に配置されている各要素の位置関係で、システムが自動的にフォーカスの移動先を判断します。
フォーカスを持っている要素の上下左右で、範囲が少しでも重なっている要素に対してフォーカスが移動します。
いっぽう、範囲内にフォーカス可能な要素がない場合はフォーカス移動できません。
例えば、以下のような位置で要素が配置されている画面では、フォーカスを持っているボタンAからボタンBにフォーカスを移動させることができません。
ボタンAとボタンCは互いにフォーカス移動が可能ですが、ボタンBは、ボタンA、Cどちらとも範囲が重なっていません。
配置を、常に上下左右の範囲内に収まるようにできればいいのですが、そうではないデザインの場合もあります。
このような場合に、UIFocusGuideを使用することで次にどの要素にフォーカスを渡すかを制御することができます。
UIFocusGuideを使用する
UIFoucusGuideは画面上の範囲を指定して配置し、そこにフォーカスが移動してきた際に、次にどの要素にフォーカスを移動させるべきかをガイドします。以下の例は、ボタンAの右側にFocusGuideを配置して、次にフォーカスされるべき要素をボタンBにしています。
※図中では概念的なものを説明するためにFocusGuideを可視化しています。実際には見えません。
コード例
class ViewController: UIViewController { … @IBOutlet rivate weak var buttonA: UIButton! @IBOutlet private weak var buttonB: UIButton! private var focusGuide: UIFocusGuide! … override func viewDidLoad() { // ボタンA の右側 10px 離れた位置に ボタンAと同じ高さ・幅1pxの領域でFocusGuideを配置 self.focusGuide = UIFocusGuide() self.view.addLayoutGuide(self.focusGuide) self.focusGuide.leftAnchor.constraint(equalTo: self.buttonA.rightAnchor, constant: 10.0).isActive = true self.focusGuide.topAnchor.constraint(equalTo: self.buttonA.topAnchor).isActive
= true self.focusGuide.widthAnchor.constraint(equalToConstant: 1.0).isActive = true self.focusGuide.heightAnchor.constraint(equalTo: self.buttonA.heightAnchor).isActive = true // 次にフォーカスさせる要素にボタンBを指定 self.focusGuide.preferredFocusedView = self.buttonB // FocusGuideを有効化 self.focusGuide.isEnabled = true } … }
これで、ボタンAにフォーカスがある時に右方向へスワイプするとフォーカスがボタンBへ移動できるようになります。
今回は、ボタンBからボタンAおよびボタンCへのフォーカス移動は考慮していませんが同じ要領で対応できます。
まとめ
FocusGuideはフォーカス移動先を自由に指定できますが、直感的に連想できない移動をさせると混乱の素です。(左にスワイプしたら右の要素にフォーカスが移動するとか、、)
基本的には直感的にフォーカスの移動先を連想できる範囲で使用するといいでしょう。