tvOS Advent Calendar 2017 4日目担当の @dekatotoroです。

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

フォーカス

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

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

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

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

UIFocusGuide

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

@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

1番左のcollectionCellからbuttonへのフォーカスはできますが
image.png

2つめ以降のcollectionCellからbuttonへのフォーカスはデフォルトではできません。
image.png

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


@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

FocusGuideを追加することによって2つめ以降のcollectionCellからbuttonへのフォーカスが可能になります。
image.png

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

まとめ

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

tvOS Advent Calendar 2017 明日は再び @toshi0383 で 「AVContentProposalは本当に便利なのか」 です!