UIFocusGuideの使い方 - tvOS -

[**tvOS Advent Calendar 2017**](https://qiita.com/advent-calendar/2017/tvos) 4日目担当の @dekatotoroです。

今日は**UIFocusGuide**の使い方についてです。

# **フォーカス**
まず、簡単にフォーカスについて説明します。

iOSとtvOSの一番大きな違いとしてフォーカスがあります。
iOSデバイスの場合、ユーザーは直接タッチスクリーン上で操作しますが、Apple TVではリモコンで画面上のある項目にナビゲートすると「フォーカスされた状態になって、ユーザーアクションの対象になるといった動作になります。

フォーカスするViewはフォーカスエンジンがを決定します。フォーカスエンジンが検索する範囲は、現在フォーカスされているViewの大きさに応じて決まり、そのViewを起点として左右上下に動きの方向にあるフォーカス可能な領域を見つけて更新します。

フォーカスエンジンはView階層のフォーカス動作を定義するUIFocusEnvironmentプロトコルに従ってフォーカスを制御しますが、UIFocusGuideを使うことでフォーカスエンジンがフォーカスするViewを動的に指定することができます。

# **UIFocusGuide**
UIFocusGuideはフォーカス用のガイドです。
UILayoutGuideを継承したクラスで、フォーカス用にpreferredFocusEnvironmentsが定義されています。

```swift
@available(tvOS 9.0, *)
open class UIFocusGuide : UILayoutGuide {

    
    /// If disabled, UIFocusGuides are ignored by the focus engine, but still participate in layout. Modifying this flag allows you to conditionally enable or disable certain focus behaviors. YES by default.
    open var isEnabled: Bool

    
    /// Setting preferredFocusEnvironments to a non-empty array marks this guide's layoutFrame as focusable. If empty, this guide is effectively disabled.
    /// If focused, the guide attempts to redirect focus to each environment in the array, in order, stopping when a focusable item in an environment has been found.
    @available(tvOS 10.0, *)
    open var preferredFocusEnvironments: [UIFocusEnvironment]!

}

```


# **UIFocusGuideの使い方**
具体的な例でUIFocusGuideの使い方を説明したいと思います。
以下のようなUIButtonとUICollectionViewのシンプルな画面を例にします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48971/ad97f6bd-3613-8023-c644-2d961becf836.png)


1番左のcollectionCellからbuttonへのフォーカスはできますが
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48971/9f5fc2be-3b15-362b-b641-6c6096195a2b.png)


2つめ以降のcollectionCellからbuttonへのフォーカスはデフォルトではできません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48971/643d5dcd-8842-7b54-ae49-ea5c1d05c40b.png)

これはフォーカスエンジンが検索する範囲は、現在フォーカスされているViewの大きさに応じて決まるからです。
このような場合FocusGuideを使ってフォーカスを制御することができます。
FocusGuideを追加してみます。

```swift

@IBOutlet private weak var button: UIButton!
@IBOutlet private weak var collectionView: UICollectionView!
private let focusGuide = UIFocusGuide()
...
override func viewDidLoad() {
    super.viewDidLoad()

    view.addLayoutGuide(focusGuide)

    focusGuide.topAnchor.constraint(equalTo: button.topAnchor).isActive = true
    focusGuide.leftAnchor.constraint(equalTo: collectionView.leftAnchor).isActive = true

    focusGuide.heightAnchor.constraint(equalTo: button.heightAnchor).isActive = true
    focusGuide.widthAnchor.constraint(equalTo: collectionView.widthAnchor).isActive = true

    focusGuide.preferredFocusEnvironments = [button]
    ...
}
```

図にすると以下のようにFocusGuideが追加されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48971/4c7a5e3c-4718-232b-e20f-eea2edb29ef6.png)

FocusGuideを追加することによって2つめ以降のcollectionCellからbuttonへのフォーカスが可能になります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/48971/d68bb643-a51a-93a8-acfe-467f9fa763b9.png)

また、focusGuide.preferredFocusEnvironmentsを条件やフォーカスれたViewによって変更するなど、動的にフォーカスの制御を行うことができます。

# **まとめ**
簡単になりますが、UIFocusGuideの説明でした。
フォーカスエンジンは現在フォーカスされているViewの大きさに応じて左右上下の範囲でフォーカスするViewを検索します。基本はフォーカスエンジンに任せておけば良いですが、デフォルトでフォーカスされないようなレイアウトの場合や、動的にフォーカスの制御を行いたい場合などにUIFocusGuideを使うと簡単に実装することができそうですね :smile: 

[**tvOS Advent Calendar 2017**](https://qiita.com/advent-calendar/2017/tvos) 明日は再び @toshi0383 で 「AVContentProposalは本当に便利なのか」 です！