Xamarin での tvOS ナビゲーションとフォーカスの操作
"この記事では、フォーカスの概念と、フォーカスを使って Xamarin.tvOS アプリ内でナビゲーションを表示および処理する方法について説明します。"
この記事では、フォーカスの概念と、それを使って Xamarin.tvOS アプリのユーザー インターフェイスでナビゲーションを処理する方法について説明しています。 組み込みの tvOS ナビゲーション コントロールでフォーカス、強調表示、選択を使って Xamarin.tvOS アプリのユーザー インターフェイス ナビゲーションを提供する方法について説明します。
次に、フォーカスを視差や "レイヤード画像" と共に使い、現在のナビゲーション状態の視覚的な手がかりをエンド ユーザーに提供する方法について説明します。
最後に、Xamarin.tvOS アプリの画像ビューでフォーカス、フォーカス更新、フォーカス ガイド、コレクションへのフォーカス、視差の有効化の操作について説明します。
ナビゲーション
Xamarin.tvOS アプリのユーザーは、iOS のようにデバイスの画面上の画像をタップしてインターフェイスを直接操作するのではなく、Siri リモート コントローラーを使って部屋の向こう側から間接的に操作します。 アプリのユーザー インターフェイスを設計するときは、自然な流れでありながらユーザーが Apple TV エクスペリエンスに没頭できるように、この点に留意する必要があります。
tvOS アプリでナビゲーションを実装するには、ナビゲーション自体に注意を引くことなく、アプリの目的とその提示するデータの構造をスムーズにサポートするようにすることをお勧めします。 ユーザー インターフェイスを支配したり、コンテンツやアプリのユーザー エクスペリエンスからフォーカスを外したりすることなく、自然で親しみやすいナビゲーションを設計します。
通常、ユーザーは Apple TV を使っている間に、それぞれに所定のコンテンツ セットが表示されている積み重ねられた一連の画面を操作します。 また、新しい画面ごとに、ボタン、タブ バー、テーブル、コレクション ビュー、分割ビューなどの標準 UI コントロールを使うコンテンツのサブ画面が 1 つ以上表示される場合があります。
データの新しい画面が表示されるたびに、ユーザーはこのような画面の積み重ねの奥深くに移動します。 Siri リモート コントローラーの [メニュー] ボタンを使うと、積み重ねを逆方向に移動して、前の画面またはメイン メニューに戻ることができます。
Apple では、tvOS アプリのナビゲーションを設計するときに次の点に留意することを推奨しています。
- コンテンツをすばやく簡単に見つけられるようにナビゲーションをレイアウトする - ユーザーは、できるだけ少ない回数のタップ、クリック、スワイプでアプリ内のコンテンツにアクセスしたいと考えています。 ナビゲーションを簡略化し、コンテンツを最小限の画面数に整理しましょう。
- タッチを使って流動的なインターフェイスを作成する - ユーザーが可能な限り少ない数のジェスチャを使い、最小限の摩擦で "フォーカス可能な項目" 間を移動できるようにします。
- フォーカスを念頭に置いて設計する - ユーザーは部屋のあちこちでコンテンツを操作しているため、Siri リモート コントローラーを使って操作する前に、フォーカスをユーザー インターフェイス項目に移動する必要があります。 目標を達成するために必要なジェスチャが多すぎると、ユーザーはアプリに不満を感じます。
- メニュー ボタンによる後方ナビゲーションを提供する - 簡単で使い慣れたエクスペリエンスを実現するために、ユーザーが Siri リモート コントローラーの [メニュー] ボタンを使って後方ナビゲーションできるようにします。 [メニュー] ボタンを押した場合、常に前の画面に戻るか、アプリのメイン メニューに戻るようにします。 アプリの最上位レベルで [メニュー] ボタンを押した場合、Apple TV のホーム画面に戻るようにします。
- 通常は [戻る] ボタンの表示を避ける - Siri リモート コントローラーの [メニュー] ボタンを押した場合、画面の積み重ねを逆方向に移動するので、この動作と重複する余計なコントロールを表示しないようにします。 この規則の例外は、購入画面や、破壊的なアクション (コンテンツの削除など) がある画面の場合です。このような場合には [キャンセル] ボタンも表示する必要があります。
- 大規模なコレクションを多数ではなく 1 つの画面に表示する - Siri リモート コントローラーは、ジェスチャを使って大規模なコンテンツ コレクションをすばやく簡単に移動できるように設計されています。 フォーカス可能な項目の大規模なコレクションをアプリで操作する場合は、ユーザー側でより多くのナビゲーションが必要になる多くの画面に項目を分割するのではなく、1 つの画面に保持することを検討してください。
- ナビゲーションに標準コントロールを使う - 繰り返しになりますが、簡単で使い慣れたユーザー エクスペリエンスを作成するには、可能な限り、アプリのナビゲーションにはページ コントロール、タブ バー、セグメント コントロール、テーブル ビュー、コレクション ビュー、分割ビューなどの組み込みの
UIKit
コントロールを使います。 ユーザーはこのような要素を既に使い慣れているため、アプリを直感的に操作できます。 - 水平方向のコンテンツ ナビゲーションを優先する - Apple TV の性質上、Siri リモート コントローラーでは、上下よりも左から右へのスワイプの方が自然です。 アプリのコンテンツ レイアウトを設計するときは、このオプションを検討してください。
フォーカスと選択
Apple TV では、画像、ボタン、またはその他の UI 要素が現在のナビゲーションのターゲットである場合、その要素は "フォーカスがある" と見なされます。
ユーザーがデバイスのタッチスクリーン上の要素を直接操作する iOS デバイスとは異なり、ユーザーは Siri リモート コントローラーを使って部屋の離れた場所から tvOS 要素を操作します。 このユーザー操作を表示および処理するために、Apple TV は "フォーカス" ベースのモデルを使っています。
ユーザーは、ジェスチャと Siri リモート コントローラーのボタンの押下を使って、UI 要素間でフォーカスを移動します。 フォーカスが目的の要素に移動すると、ユーザーはクリックしてコンテンツを選ぶか、その要素が表すアクションをアクティブにします。
フォーカスが変化したら、さりげないアニメーションと効果 (画像の視差効果など) を使って、現在フォーカスがあるユーザー インターフェイス項目を明確に示します。
Apple は、フォーカスと選択を操作するために次のように提案しています。
- モーション効果に組み込みの UI コントロールを使う - ユーザー インターフェイスで
UIKit
とフォーカス API を使うと、フォーカス モデルにより、既定のモーション効果と視覚効果が UI 要素に自動的に適用されます。 こうすることで、アプリは Apple TV プラットフォームのユーザーにとってネイティブで使い慣れたものになり、フォーカス可能な項目間での流動的かつ直感的な移動が可能になります。 - 期待される方向にフォーカスを移動する - Apple TV では、ほぼすべての要素が間接的な操作を使います。 たとえば、ユーザーが Siri リモート コントローラーを使ってフォーカスを移動すると、システムはインターフェイスを自動的にスクロールして、現在フォーカスのある項目を表示したままにします。 アプリがこの種類の操作を実装している場合は、ユーザーのジェスチャの方向にフォーカスが移動するようにします。 そのため、ユーザーが Siri リモート コントローラーで右にスワイプした場合、フォーカスは右に移動するようにします (その結果、画面が左にスクロールする可能性があります)。 この規則の 1 つの例外は、直接操作 (上にスワイプすると要素が上に移動する) を使う全画面項目です。
- フォーカスのある項目を明確にする - ユーザーは遠くから UI 要素を操作しているため、現在フォーカスのある項目を目立つようにすることが重要です。通常、これは組み込みの
UIKit
要素によって自動的に処理されます。 カスタム コントロールの場合は、項目のサイズや影などの機能を使ってフォーカスを表示します。 - 視差を使ってフォーカスのある項目の応答性を高める - Siri リモート コントローラーで小さな円を描くジェスチャを行うと、フォーカスのある項目が緩やかにリアルタイムで動きます。 この Parallax Effect は
UIKit
Layered Images に組み込まれており、ユーザーにフォーカスされたアイテムへの接続感を与えます。 - 適切なサイズのフォーカス可能な項目を作成する - 十分な間隔がある大きな項目は、小さな項目よりも選択や移動が簡単です。
- フォーカスのある項目でもフォーカスのない項目でも見栄え良く UI 要素をデザインする - 通常、Apple TV は、フォーカスのある項目をサイズを大きくすることで表します。 アプリの UI 要素がどのようなプレゼンテーション サイズでも適切に表示されることを確認し、必要に応じて、より大きなサイズの要素に対して資産を提供します。
- フォーカスの変化を流動的に表現する - アニメーションを使って項目のフォーカスのある状態とフォーカスのない状態の間をスムーズにフェードし、切り替えが不快にならないようにします。
- カーソルを表示しない - ユーザーは、画面上でカーソルを移動するのではなく、フォーカスを使ってアプリの UI を操作することを想定しています。 ユーザー インターフェイスでは、一貫したユーザー エクスペリエンスを提供するために、常にフォーカス モデルを使うようにします。
フォーカスの操作
フォーカス可能な項目となるカスタム コントロールを作成する場合があります。 その場合は CanBecomeFocused
プロパティをオーバーライドして true
を返し、それ以外の場合は false
を返します。 次に例を示します。
public class myView : UIView
{
public override bool CanBecomeFocused {
get {return true;}
}
}
いつでも、UIKit
コントロールの Focused
プロパティを使って、それが現在の項目かどうかを確認できます。 true
の場合、UI 項目には現在フォーカスがあり、それ以外の場合はフォーカスがありません。 次に例を示します。
// Is my view in focus?
if (myView.Focused) {
// Do something
...
}
コードを介して別の UI 要素にフォーカスを直接移動することはできませんが、画面を読み込むときに最初にフォーカスを得る UI 要素を指定できます。その際にその PreferredFocusedView
プロパティを true
に設定します。 次に例を示します。
// Make the play button the starting focus item
playButton.PreferredFocusedView = true;
フォーカスの更新の操作
ユーザーが (たとえば、Siri リモート コントローラーでのジェスチャに応答するなどして) UI 要素間でフォーカスを移動すると、フォーカスを失った項目とフォーカスを得た項目の両方に "フォーカス更新イベント" が送信されます。
UIView
または UIViewController
から継承するカスタム要素の場合、いくつかのメソッドをオーバーライドして、フォーカス更新イベントを操作できます。
- DidUpdateFocus - このメソッドは、ビューがフォーカスを得るか失うたびに呼び出されます。
- ShouldUpdateFocus - このメソッドを使って、フォーカスの移動を許可する場所を定義します。
フォーカス エンジンがフォーカスを PreferredFocusedView
UI 要素に戻すように要求するには、ビュー コントローラーの SetNeedsUpdateFocus
メソッドを呼び出します。
重要
SetNeedsUpdateFocus
の呼び出しは、呼び出し対象のビュー コントローラーに、現在フォーカスがあるビューが含まれている場合にのみ効果があります。
フォーカス ガイドの操作
tvOS に組み込まれているフォーカス エンジンは、水平および垂直グリッド上にある項目間の動きを処理するのに優れています。 通常は、インターフェイス デザインに UI 要素を追加するだけで済みます。開発者が介入しなくても、これらの要素間の移動はフォーカス エンジンによって自動的に処理されます。
ただし、UI デザインの必要性から、UI 要素が水平および垂直のグリッドに収まらず、互いに対角線上にあるためにアクセスできない場合があります。 このようなことが起こるのは、UI 項目間の単純な上下左右の移動のみを処理するようにフォーカス エンジンが設計されているためです。
例として次の UI レイアウトを使います。
[詳細情報] ボタンは [購入] ボタンの水平および垂直グリッド上に配置されていないため、ユーザーからはアクセスしづらい位置にあります。 ただし、これは、"フォーカス ガイド" を使ってフォーカス エンジンに動きのヒントを提供することで簡単に修正できます。
フォーカス ガイド (UIFocusGuide
) では、ビューの非表示領域をフォーカス可能としてフォーカス エンジンに公開しているため、フォーカスを別のビューにリダイレクトできます。
そのため、上の例を解決するには、次のコードをビュー コントローラーに追加して、[詳細情報] ボタンと [購入] ボタンの間にフォーカス ガイドを作成します。
public UIFocusGuide FocusGuide = new UIFocusGuide ();
...
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Add Focus Guide to layout
View.AddLayoutGuide (FocusGuide);
// Define Focus Guide that will allow the user to move
// between the More Info and Buy buttons.
FocusGuide.LeftAnchor.ConstraintEqualTo (BuyButton.LeftAnchor).Active = true;
FocusGuide.TopAnchor.ConstraintEqualTo (MoreInfoButton.TopAnchor).Active = true;
FocusGuide.WidthAnchor.ConstraintEqualTo (BuyButton.WidthAnchor).Active = true;
FocusGuide.HeightAnchor.ConstraintEqualTo (MoreInfoButton.HeightAnchor).Active = true;
}
まず、新しい UIFocusGuide
が作成され、AddLayoutGuide
メソッドを使ってビューのレイアウト ガイド コレクションに追加されます。
次に、フォーカス ガイドの上、左、幅、高さのアンカーが [詳細情報] ボタンと [購入] ボタンの間に配置されるように調整されます。 参照トピック
新しい制約は、Active
プロパティを true
に設定することで作成されたときに、アクティブになることに注意することも重要です。
FocusGuide.LeftAnchor.ConstraintEqualTo (...).Active = true;
新しいフォーカス ガイドが確立され、ビューに追加されると、ビュー コントローラーの DidUpdateFocus
メソッドをオーバー ライドして、[詳細情報] ボタンと [購入] ボタンの間を移動する次のコードを追加できます。
public override void DidUpdateFocus (UIFocusUpdateContext context, UIFocusAnimationCoordinator coordinator)
{
base.DidUpdateFocus (context, coordinator);
// Get next focusable item from context
var nextFocusableItem = context.NextFocusedView;
// Anything to process?
if (nextFocusableItem == null) return;
// Decide the next focusable item based on the current
// item with focus
if (nextFocusableItem == MoreInfoButton) {
// Move from the More Info to Buy button
FocusGuide.PreferredFocusedView = BuyButton;
} else if (nextFocusableItem == BuyButton) {
// Move from the Buy to the More Info button
FocusGuide.PreferredFocusedView = MoreInfoButton;
} else {
// No valid move
FocusGuide.PreferredFocusedView = null;
}
}
まず、このコードは、渡された UIFocusUpdateContext
から NextFocusedView
を取得します (context
)。 このビューが null
の場合、処理は必要なく、メソッドは終了します。
次に、nextFocusableItem
が評価されます。 [詳細情報] または [購入] ボタンのいずれかに一致する場合、フォーカス ガイドの PreferredFocusedView
プロパティを使って、フォーカスが反対側のボタンに送信されます。 次に例を示します。
// Move from the More Info to Buy button
FocusGuide.PreferredFocusedView = BuyButton;
どちらのボタンもフォーカス シフトのソースではない場合、PreferredFocusedView
プロパティはクリアされます。
// No valid move
FocusGuide.PreferredFocusedView = null;
コレクションでのフォーカスの操作
個々の項目を UICollectionView
または UITableView
でフォーカス可能にするかどうかを決定するときは、それぞれ UICollectionViewDelegate
または UITableViewDelegate
のメソッドをオーバーライドします。 次に例を示します。
public class CardHandDelegate : UICollectionViewDelegateFlowLayout
{
...
public override bool CanFocusItem (UICollectionView collectionView, NSIndexPath indexPath)
{
if (indexPath == null) {
return false;
} else {
var controller = collectionView as CardHandViewController;
return !controller.Hand [indexPath.Row].IsFaceDown;
}
}
}
CanFocusItem
メソッドは、現在の項目にフォーカスできる場合は true
を返し、そうでない場合は false
を返します。
UICollectionView
または UITableView
がフォーカスを失って再び得たときに最後の項目を記憶し、フォーカスを復元するようにするには、RemembersLastFocusedIndexPath
プロパティを true
に設定します。
フォーカスと視差
前述のように、ユーザー インターフェイス要素は、ナビゲーション イベント中の現在の項目である場合、"フォーカスがある" と見なされます。 通常、項目にフォーカスが移動すると、そのサイズはわずかに大きくなり、シャドウを使って視覚的に強調されます。
ユーザーが Siri リモート コントローラーでゆっくりと円を描くジェスチャを行うと、フォーカスのある項目はこの動きに応じてリアルタイムで揺れます。 揺れが発生すると、照らされた光沢が画像に適用され、画面が輝いているように見えます。 非アクティブな状態が一定時間続くと、フォーカスのないコンテンツは暗くなり、フォーカスのある項目はさらに大きくなります。
これらの効果が連携することで、テレビ画面上のコンテンツと、ソファから Apple TV を操作するユーザーとの間に精神的なつながりをもたらします。
Apple TV では、この視差効果はシステム全体で使われ、フォーカスのある項目に 3 次元の深度とダイナミクスの感覚を伝えます。 tvOS は、一連の透明なレイヤード画像を使い、動的に移動および拡大縮小してこの効果を生み出します。
レイヤード画像は tvOS アプリのアイコンに必要であり、動的なトップ シェルフ コンテンツでサポートされています。 必須ではありませんが、Apple では、アプリの UI 内にある他のフォーカス可能な項目にレイヤード画像を実装することを強く推奨しています。
視差の有効化
UIImageView
コントロール (または UIImageView
から継承する任意のコントロール) は、視差効果を自動的にサポートします。 既定では、このサポートは無効になっています。有効にするには、次のコードを使います。
myImageView.AdjustsImageWhenAncestorFocused = true;
このプロパティを true
に設定すると、ユーザーが選び、フォーカスを得たときに、イメージ ビューは視差効果を自動的に得ます。
まとめ
この記事では、フォーカスの概念と、それを使って Xamarin.tvOS アプリのユーザー インターフェイスでナビゲーションを処理する方法について説明しました。 組み込みの tvOS ナビゲーション コントロールがフォーカス、強調表示、選択を使ってナビゲーションを提供する方法を確認しました。 次に、フォーカスを視差とレイヤード画像と共に使い、現在のナビゲーション状態の視覚的な手がかりをエンド ユーザーに提供する方法を確認しました。 最後に、フォーカス、フォーカス更新、コレクション内のフォーカス、視差の有効化の操作について確認しました。