Xamarin で tvOS テキスト フィールドと検索フィールドを操作する

必要に応じて、Xamarin.tvOS アプリは、テキスト フィールドとスクリーン キーボードを使用して、ユーザー (ユーザー ID やパスワードなど) に小さなテキストを要求できます。

サンプル検索フィールド

必要に応じて、検索フィールドを使用してアプリのコンテンツのキーワード検索機能を提供できます。

検索結果のサンプル

このドキュメントでは、Xamarin.tvOS アプリでのテキスト フィールドと検索フィールドの操作の詳細について説明します。

テキスト フィールドと検索フィールドについて

前述のように、必要に応じて、Xamarin.tvOS は 1 つ以上のテキスト フィールドを表示して、画面上 (またはユーザーがインストールした tvOS のバージョンによってはオプションの Bluetooth キーボード) を使用してユーザーから少量のテキストを収集できます。

さらに、アプリがユーザーに大量のコンテンツ (音楽、映画、画像コレクションなど) を表示する場合は、ユーザーが少量のテキストを入力して使用可能な項目の一覧をフィルター処理できるようにする検索フィールドを含めることができます。

テキスト フィールド

tvOS では、テキスト フィールドは、高さが固定され、丸みを帯びた角の入力ボックスとして表示され、ユーザーがクリックしたときにスクリーン キーボードが表示されます。

tvOS のテキスト フィールド

ユーザーが特定のテキスト フィールドにフォーカスを移動すると、大きくなり、深い影が表示されます。 ユーザー インターフェイスを設計するときは、この点に留意する必要があります。テキスト フィールドはフォーカスがあるときに他の UI 要素と重なることがあるためです。

Apple は、テキスト フィールドの操作に関して次のように推奨しています。

  • テキスト入力を控えめに使用する - スクリーン キーボードの性質上、長いテキストを入力したり、複数のテキスト フィールドに入力したりするのはユーザーにとって面倒です。 より良い解決策は、選択リストまたはボタンを使用してテキスト入力の量を制限することです。
  • ヒントを使用して目的を伝える - テキスト フィールドは、空の場合に "ヒント" というプレースホルダーを表示できます。 適用可能であれば、別のラベルではなくヒントを使用して、テキスト フィールドの目的を説明します。
  • 適切な既定のキーボードの種類を選択する - tvOS には、テキスト フィールドに指定できる目的別の組み込みキーボードの種類がいくつか用意されています。 たとえば、電子メール アドレス キーボードは、ユーザーが最近入力したアドレスの一覧から選択できるようにすることで、入力を容易にすることができます。
  • 必要に応じて、セキュリティで保護されたテキスト フィールドを使用する - セキュリティで保護されたテキスト フィールドには、入力された文字が (実際の文字ではなく) ドットとして表示されます。 パスワードなどの機密情報を収集するときは、常にセキュリティで保護されたテキスト フィールドを使用します。

キーボード

ユーザーがユーザー インターフェイスのテキスト フィールドをクリックするたびに、線形のスクリーン キーボードが表示されます。 ユーザーは Siri Remote の Touch サーフェスを使用して、キーボードから個々の文字を選択し、要求された情報を入力します。

Siri リモート キーボード

現在のビューに複数のテキスト フィールドがある場合は、[次へ] ボタンが自動的に表示され、ユーザーは次のテキスト フィールドに移動します。 最後のテキスト フィールドには [完了] ボタンが表示されます。これにより、テキスト入力が終了し、ユーザーが前の画面に戻ります。

ユーザーはいつでも Siri Remote の[MENU] ボタンを押してテキスト入力を終了し、もう一度前の画面に戻ることもできます。

Apple は、スクリーン キーボードの操作に関して次のように推奨しています。

  • 適切な既定のキーボードの種類を選択する - tvOS には、テキスト フィールドに指定できる目的別の組み込みキーボードの種類がいくつか用意されています。 たとえば、電子メール アドレス キーボードは、ユーザーが最近入力したアドレスの一覧から選択できるようにすることで、入力を容易にすることができます。
  • 必要に応じて、キーボード アクセサリ ビューを使用する - 常に表示される標準情報に加えて、オプションのアクセサリ ビュー (画像やラベルなど) をスクリーン キーボードに追加して、テキスト入力の目的を明確にしたり、ユーザーが必要な情報を入力するのを支援したりできます。

スクリーン キーボードの操作の詳細については、Apple の UIKeyboardTypeキーボードの管理データ入力のカスタム ビューiOS のテキスト プログラミング ガイドに関するドキュメントをご覧ください。

検索フィールドには、テキスト フィールドとスクリーン キーボードを提供する特殊な画面が表示され、ユーザーはキーボードの下に表示される項目のコレクションをフィルター処理できます。

検索結果のサンプル

ユーザーが検索フィールドに文字を入力すると、以下の結果に検索結果が自動的に反映されます。 ユーザーはいつでも結果にフォーカスを移動し、表示される項目のいずれかを選択できます。

Apple には、検索フィールドを操作するための次の推奨事項があります。

  • 最近の検索を提供する - Siri Remote でのテキスト入力は面倒な場合があり、ユーザーは検索要求を繰り返す傾向があるため、キーボード領域の下の現在の結果の前に最近の検索結果のセクションを追加することをご検討ください。
  • 可能な場合は、結果の数を制限する - ユーザーが大きな項目リストを解析および移動するのは困難な場合があるため、返される結果の数を制限することをご検討ください。
  • 必要に応じて、検索結果フィルターを提供する - アプリによって提供されるコンテンツがそれ自体に適している場合は、返された検索結果をさらにフィルター処理できるようにスコープ バーを追加することをご検討ください。

詳細については、Apple の UISearchController クラス リファレンスに関する記事をご覧ください。

テキスト フィールドの操作

Xamarin.tvOS アプリでテキスト フィールドを操作する最も簡単な方法は、iOS デザイナーを使用してユーザー インターフェイス デザインに追加することです。

次の操作を行います。

  1. Solution PadMain.storyboard ファイルをダブルクリックして、編集用に開きます。

  2. デザイン サーフェイスに 1 つ以上のテキスト フィールドをドラッグしてビューに移動します。

    テキスト フィールド

  3. テキスト フィールドを選び、Properties Pad[ウィジェット] タブで一意の [名前] を付けます。

    Properties Pad の [ウィジェット] タブ

  4. [テキスト フィールド] セクションでは、プレースホルダー ヒントや既定のなどの要素を定義できます。

    [テキスト フィールド] セクション

  5. 下にスクロールして、[スペル チェック][大文字と小文字]、既定の [キーボードの種類] などのプロパティを定義します。

    スペル チェック、大文字と小文字、および既定のキーボードの種類

  6. ストーリーボードへの変更を保存します。

コードでは、Text プロパティを使用してテキスト フィールドの値を取得または設定できます。

Console.WriteLine ("User ID {0} and Password {1}", UserId.Text, Password.Text);

必要に応じて、StartedEnded テキスト フィールド イベントを使用して、テキスト入力の開始と終了に応答できます。

検索フィールドの操作

Xamarin.tvOS アプリで検索フィールドを操作する最も簡単な方法は、インターフェイス デザイナーを使用してユーザー インターフェイス デザインにフィールドを追加することです。

次の操作を行います。

  1. Solution PadMain.storyboard ファイルをダブルクリックして、編集用に開きます。

  2. 新しいコレクション ビュー コントローラーをストーリーボードにドラッグして、ユーザーの検索結果を表示します。

    コレクション ビュー コントローラー

  3. Properties Pad[ウィジェット] タブで、[クラス]SearchResultsViewController を使用し、[ストーリーボード ID]SearchResults を使用します。

    Visual Studio for Mac の [ウィジェット] タブ。クラスとストーリーボード ID を指定できます。

  4. デザイン サーフェイスで [セル プロトタイプ] を選びます。

  5. [プロパティ] エクスプローラーの [ウィジェット] タブで、[クラス]SearchResultCell を使用し、[識別子]ImageCell を使用します。

    クラスと識別子を指定できる Visual Studio for Mac の [ウィジェット] タブ。

  6. [セル プロトタイプ] のデザインをレイアウトし、[プロパティ] エクスプローラーの [ウィジェット] タブで、各要素を一意の [名前] で公開します。

    セル プロトタイプのデザインをレイアウトする

  7. ストーリーボードへの変更を保存します。

データ モデルを指定する

次に、ユーザーが検索する結果のデータ モデルとして機能するクラスを提供する必要があります。 ソリューション エクスプローラーで [プロジェクト名] を右クリックし、[追加]>[新しいファイル...]>[全般]>[空のクラス] の順に選び、[名前] を指定します。

[空のクラス] を選択し、[名前] を指定します

たとえば、ユーザーがタイトルとキーワードで画像のコレクションを検索できるようにするアプリは、次のようになります。

using System;
using Foundation;

namespace tvText
{
    public class PictureInformation : NSObject
    {
        #region Computed Properties
        public string Title { get; set;}
        public string ImageName { get; set;}
        public string Keywords { get; set;}
        #endregion

        #region Constructors
        public PictureInformation (string title, string imageName, string keywords)
        {
            // Initialize
            this.Title = title;
            this.ImageName = imageName;
            this.Keywords = keywords;
        }
        #endregion
    }
}

コレクション ビューのセル

データ モデルを配置したので、[プロトタイプ] セル (SearchResultViewCell.cs) を編集し、次のように表示します。

using Foundation;
using System;
using UIKit;

namespace tvText
{
    public partial class SearchResultViewCell : UICollectionViewCell
    {
        #region Private Variables
        private PictureInformation _pictureInfo = null;
        #endregion

        #region Computed Properties
        public PictureInformation PictureInfo {
            get { return _pictureInfo; }
            set {
                _pictureInfo = value;
                UpdateUI ();
            }
        }
        #endregion

        #region Constructors
        public SearchResultViewCell (IntPtr handle) : base (handle)
        {
            // Initialize
            UpdateUI ();
        }
        #endregion

        #region Private Methods
        private void UpdateUI ()
        {
            // Anything to process?
            if (PictureInfo == null) return;

            try {
                Picture.Image = UIImage.FromBundle (PictureInfo.ImageName);
                Picture.AdjustsImageWhenAncestorFocused = true;
                Title.Text = PictureInfo.Title;
                TextColor = UIColor.LightGray;
            } catch {
                // Ignore errors if view isn't fully loaded
            }
        }
        #endregion
    }

}

この UpdateUI メソッドは、プロパティが更新されるたびに、名前付き UI 要素の PictureInformation 項目 (PictureInfo プロパティ) の個々のフィールドを表示するために使用されます。 たとえば、画像に関連付けられているイメージとタイトルです。

コレクション ビュー コントローラー

次に、検索結果コレクション ビュー コントローラー (SearchResultsViewController.cs) を編集し、次のようにします。

using Foundation;
using System;
using UIKit;
using System.Collections.Generic;

namespace tvText
{
    public partial class SearchResultsViewController : UICollectionViewController , IUISearchResultsUpdating
    {
        #region Constants
        public const string CellID = "ImageCell";
        #endregion

        #region Private Variables
        private string _searchFilter = "";
        #endregion

        #region Computed Properties
        public List<PictureInformation> AllPictures { get; set;}
        public List<PictureInformation> FoundPictures { get; set; }
        public string SearchFilter {
            get { return _searchFilter; }
            set {
                _searchFilter = value.ToLower();
                FindPictures ();
                CollectionView?.ReloadData ();
            }
        }
        #endregion

        #region Constructors
        public SearchResultsViewController (IntPtr handle) : base (handle)
        {
            // Initialize
            this.AllPictures = new List<PictureInformation> ();
            this.FoundPictures = new List<PictureInformation> ();
            PopulatePictures ();
            FindPictures ();

        }
        #endregion

        #region Private Methods
        private void PopulatePictures ()
        {
            // Clear list
            AllPictures.Clear ();

            // Add images
            AllPictures.Add (new PictureInformation ("Antipasta Platter","Antipasta","cheese,grapes,tomato,coffee,meat,plate"));
            AllPictures.Add (new PictureInformation ("Cheese Plate", "CheesePlate", "cheese,plate,bread"));
            AllPictures.Add (new PictureInformation ("Coffee House", "CoffeeHouse", "coffee,people,menu,restaurant,cafe"));
            AllPictures.Add (new PictureInformation ("Computer and Expresso", "ComputerExpresso", "computer,coffee,expresso,phone,notebook"));
            AllPictures.Add (new PictureInformation ("Hamburger", "Hamburger", "meat,bread,cheese,tomato,pickle,lettus"));
            AllPictures.Add (new PictureInformation ("Lasagna Dinner", "Lasagna", "salad,bread,plate,lasagna,pasta"));
            AllPictures.Add (new PictureInformation ("Expresso Meeting", "PeopleExpresso", "people,bag,phone,expresso,coffee,table,tablet,notebook"));
            AllPictures.Add (new PictureInformation ("Soup and Sandwich", "SoupAndSandwich", "soup,sandwich,bread,meat,plate,tomato,lettus,egg"));
            AllPictures.Add (new PictureInformation ("Morning Coffee", "TabletCoffee", "tablet,person,man,coffee,magazine,table"));
            AllPictures.Add (new PictureInformation ("Evening Coffee", "TabletMagCoffee", "tablet,magazine,coffee,table"));
        }

        private void FindPictures ()
        {
            // Clear list
            FoundPictures.Clear ();

            // Scan each picture for a match
            foreach (PictureInformation picture in AllPictures) {
                if (SearchFilter == "") {
                    // If no search term, everything matches
                    FoundPictures.Add (picture);
                } else if (picture.Title.Contains (SearchFilter) || picture.Keywords.Contains (SearchFilter)) {
                    // If the search term is in the title or keywords, we've found a match
                    FoundPictures.Add (picture);
                }
            }
        }
        #endregion

        #region Override Methods
        public override nint NumberOfSections (UICollectionView collectionView)
        {
            // Only one section in this collection
            return 1;
        }

        public override nint GetItemsCount (UICollectionView collectionView, nint section)
        {
            // Return the number of matching pictures
            return FoundPictures.Count;
        }

        public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
        {
            // Get a new cell and return it
            var cell = collectionView.DequeueReusableCell (CellID, indexPath);
            return (UICollectionViewCell)cell;
        }

        public override void WillDisplayCell (UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath)
        {
            // Grab the cell
            var currentCell = cell as SearchResultViewCell;
            if (currentCell == null)
                throw new Exception ("Expected to display a `SearchResultViewCell`.");

            // Display the current picture info in the cell
            var item = FoundPictures [indexPath.Row];
            currentCell.PictureInfo = item;
        }

        public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
        {
            // If this Search Controller was presented as a modal view, close
            // it before continuing
            // DismissViewController (true, null);

            // Grab the picture being selected and report it
            var picture = FoundPictures [indexPath.Row];
            Console.WriteLine ("Selected: {0}", picture.Title);
        }

        public void UpdateSearchResultsForSearchController (UISearchController searchController)
        {
            // Save the search filter and update the Collection View
            SearchFilter = searchController.SearchBar.Text ?? string.Empty;
        }

        public override void DidUpdateFocus (UIFocusUpdateContext context, UIFocusAnimationCoordinator coordinator)
        {
            var previousItem = context.PreviouslyFocusedView as SearchResultViewCell;
            if (previousItem != null) {
                UIView.Animate (0.2, () => {
                    previousItem.TextColor = UIColor.LightGray;
                });
            }

            var nextItem = context.NextFocusedView as SearchResultViewCell;
            if (nextItem != null) {
                UIView.Animate (0.2, () => {
                    nextItem.TextColor = UIColor.Black;
                });
            }
        }
        #endregion
    }
}

まず、IUISearchResultsUpdating ユーザーによって更新される検索コントローラー フィルターを処理するために、インターフェイスがクラスに追加されます。

public partial class SearchResultsViewController : UICollectionViewController , IUISearchResultsUpdating

定数は、コレクション コントローラーが新しいセルを要求するときに後で使用されるプロトタイプ セルの ID (上記のインターフェイス デザイナーで定義されている ID と一致する) を指定するためにも定義されます。

public const string CellID = "ImageCell";

ストレージは、検索対象のアイテムの完全な一覧、検索フィルター用語、およびその用語に一致する項目の一覧用に作成されます。

private string _searchFilter = "";
...

public List<PictureInformation> AllPictures { get; set;}
public List<PictureInformation> FoundPictures { get; set; }
public string SearchFilter {
    get { return _searchFilter; }
    set {
        _searchFilter = value.ToLower();
        FindPictures ();
        CollectionView?.ReloadData ();
    }
}

SearchFilter が変更されると、一致する項目の一覧が更新され、コレクション ビューのコンテンツが再読み込みされます。 この FindPictures ルーチンは、新しい検索用語に一致する項目を検索する役割を担います。

private void FindPictures ()
{
    // Clear list
    FoundPictures.Clear ();

    // Scan each picture for a match
    foreach (PictureInformation picture in AllPictures) {
        if (SearchFilter == "") {
            // If no search term, everything matches
            FoundPictures.Add (picture);
        } else if (picture.Title.Contains (SearchFilter) || picture.Keywords.Contains (SearchFilter)) {
            // If the search term is in the title or keywords, we've found a match
            FoundPictures.Add (picture);
        }
    }
}

ユーザーが検索コントローラーのフィルターを変更すると、SearchFilter の値が更新されます (結果コレクション ビューが更新されます)。

public void UpdateSearchResultsForSearchController (UISearchController searchController)
{
    // Save the search filter and update the Collection View
    SearchFilter = searchController.SearchBar.Text ?? string.Empty;
}

PopulatePictures メソッドは、最初に使用可能な項目のコレクションを設定します。

private void PopulatePictures ()
{
    // Clear list
    AllPictures.Clear ();

    // Add images
    AllPictures.Add (new PictureInformation ("Antipasta Platter","Antipasta","cheese,grapes,tomato,coffee,meat,plate"));
    ...
}

この例では、コレクション ビュー コントローラーが読み込まれるときに、すべてのサンプル データがメモリ内に作成されます。 実際のアプリでは、このデータはおそらくデータベースまたは Web サービスから読み取られ、Apple TV の限られたメモリをオーバーランしないようにするために必要な場合に限られます。

NumberOfSectionsGetItemsCount メソッドは、一致する項目の数を提供します。

public override nint NumberOfSections (UICollectionView collectionView)
{
    // Only one section in this collection
    return 1;
}

public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
    // Return the number of matching pictures
    return FoundPictures.Count;
}

GetCell メソッドは、コレクション ビューの各項目について、(ストーリーボードで上記で定義した CellID に基づいて) 新しいプロトタイプ セルを返します。

public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
    // Get a new cell and return it
    var cell = collectionView.DequeueReusableCell (CellID, indexPath);
    return (UICollectionViewCell)cell;
}

この WillDisplayCell メソッドは、セルが表示される前に呼び出されるため、構成できます。

public override void WillDisplayCell (UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath)
{
    // Grab the cell
    var currentCell = cell as SearchResultViewCell;
    if (currentCell == null)
        throw new Exception ("Expected to display a `SearchResultViewCell`.");

    // Display the current picture info in the cell
    var item = FoundPictures [indexPath.Row];
    currentCell.PictureInfo = item;
}

DidUpdateFocus メソッドは、結果コレクション ビューの項目を強調表示すると、ユーザーに視覚的なフィードバックを提供します。

public override void DidUpdateFocus (UIFocusUpdateContext context, UIFocusAnimationCoordinator coordinator)
{
    var previousItem = context.PreviouslyFocusedView as SearchResultViewCell;
    if (previousItem != null) {
        UIView.Animate (0.2, () => {
            previousItem.TextColor = UIColor.LightGray;
        });
    }

    var nextItem = context.NextFocusedView as SearchResultViewCell;
    if (nextItem != null) {
        UIView.Animate (0.2, () => {
            nextItem.TextColor = UIColor.Black;
        });
    }
}

最後に、ItemSelected メソッドは、結果コレクション ビューで項目を選択するユーザー操作 (Siri Remote で Touch サーフェスをクリックする) を処理します。

public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
{
    // If this Search Controller was presented as a modal view, close
    // it before continuing
    // DismissViewController (true, null);

    // Grab the picture being selected and report it
    var picture = FoundPictures [indexPath.Row];
    Console.WriteLine ("Selected: {0}", picture.Title);
}

検索フィールドがモーダル ダイアログ ビュー (呼び出し元のビューの上部) として表示された場合は、DismissViewController メソッドを使用してユーザーがアイテムを選択したときに検索ビューを閉じます。 この例では、検索フィールドは [タブ ビュー] タブの内容として表示されるため、ここでは非表示になりません。

コレクション ビューの詳細については、コレクション ビューの操作に関するドキュメントをご覧ください。

検索フィールドの表示

検索フィールド (およびそれに関連付けられているスクリーン キーボードと検索結果) を tvOS でユーザーに表示するには、主に次の 2 つの方法があります。

  • モーダル ダイアログ ビュー - 検索フィールドは、全画面表示のモーダル ダイアログ ビューとして、現在のビューとビュー コントローラーに表示できます。 これは通常、ユーザーによるボタンやその他の UI 要素のクリック操作の応答として行われます。 ユーザーが検索結果から項目を選択すると、ダイアログは閉じます。
  • ビュー コンテンツ - 検索フィールドは、特定のビューの直接の部分です。 たとえば、タブ ビュー コントローラーの検索タブの内容などです。

上記の画像の検索可能なリストの例では、検索フィールドは [検索] タブの [コンテンツの表示] として表示され、[検索] タブ ビュー コントローラーは次のようになります。

using System;
using UIKit;

namespace tvText
{
    public partial class SecondViewController : UIViewController
    {
        #region Constants
        public const string SearchResultsID = "SearchResults";
        #endregion

        #region Computed Properties
        public SearchResultsViewController ResultsController { get; set;}
        #endregion

        #region Constructors
        public SecondViewController (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Private Methods
        public void ShowSearchController ()
        {
            // Build an instance of the Search Results View Controller from the Storyboard
            ResultsController = Storyboard.InstantiateViewController (SearchResultsID) as SearchResultsViewController;
            if (ResultsController == null)
                throw new Exception ("Unable to instantiate a SearchResultsViewController.");

            // Create an initialize a new search controller
            var searchController = new UISearchController (ResultsController) {
                SearchResultsUpdater = ResultsController,
                HidesNavigationBarDuringPresentation = false
            };

            // Set any required search parameters
            searchController.SearchBar.Placeholder = "Enter keyword (e.g. coffee)";

            // The Search Results View Controller can be presented as a modal view
            // PresentViewController (searchController, true, null);

            // Or in the case of this sample, the Search View Controller is being
            // presented as the contents of the Search Tab directly. Use either one
            // or the other method to display the Search Controller (not both).
            var container = new UISearchContainerViewController (searchController);
            var navController = new UINavigationController (container);
            AddChildViewController (navController);
            View.Add (navController.View);
        }
        #endregion

        #region Override Methods
        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();

            // If the Search Controller is being displayed as the content
            // of the search tab, include it here.
            ShowSearchController ();
        }

        public override void ViewDidAppear (bool animated)
        {
            base.ViewDidAppear (animated);

            // If the Search Controller is being presented as a modal view,
            // call it here to display it over the contents of the Search
            // tab.
            // ShowSearchController ();
        }
        #endregion
    }
}

最初に、インターフェイス デザイナーで検索結果コレクション ビュー コントローラーに割り当てられたストーリーボード識別子と一致する定数が定義されます。

public const string SearchResultsID = "SearchResults";

次に、このメソッドは ShowSearchController 新しい検索ビュー コレクション コントローラーを作成し、必要だった情報を表示します。

public void ShowSearchController ()
{
    // Build an instance of the Search Results View Controller from the Storyboard
    ResultsController = Storyboard.InstantiateViewController (SearchResultsID) as SearchResultsViewController;
    if (ResultsController == null)
        throw new Exception ("Unable to instantiate a SearchResultsViewController.");

    // Create an initialize a new search controller
    var searchController = new UISearchController (ResultsController) {
        SearchResultsUpdater = ResultsController,
        HidesNavigationBarDuringPresentation = false
    };

    // Set any required search parameters
    searchController.SearchBar.Placeholder = "Enter keyword (e.g. coffee)";

    // The Search Results View Controller can be presented as a modal view
    // PresentViewController (searchController, true, null);

    // Or in the case of this sample, the Search View Controller is being
    // presented as the contents of the Search Tab directly. Use either one
    // or the other method to display the Search Controller (not both).
    var container = new UISearchContainerViewController (searchController);
    var navController = new UINavigationController (container);
    AddChildViewController (navController);
    View.Add (navController.View);
}

上記のメソッドでは、ストーリーボードから SearchResultsViewController がインスタンス化されると、検索フィールドとスクリーン キーボードをユーザーに表示する新しい UISearchController メソッドが作成されます。 このキーボードの下に、(SearchResultsViewController によって定義された) 検索結果コレクションが表示されます。

次に、SearchBarプレースホルダー ヒントなどの情報を使用して構成します。 これにより、実行される検索の種類に関する情報がユーザーに提供されます。

検索フィールドは、次の 2 つの方法のいずれかでユーザーに表示されます。

  • モーダル ダイアログ ビュー - PresentViewController メソッドが呼び出され、既存のビューに対する検索が全画面表示で表示されます。
  • ビュー コンテンツ - 検索コントローラーを含む UISearchContainerViewController が作成されます。 検索コンテナーを包含するための UINavigationController が作成され、ナビゲーション コントローラーがビュー コントローラー AddChildViewController (navController) に追加され、ビューが View.Add (navController.View) を表示します。

最後に、プレゼンテーションの種類に基づいて、ViewDidLoad または ViewDidAppear メソッドが ShowSearchController メソッドを呼び出してユーザーに検索を表示します。

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // If the Search Controller is being displayed as the content
    // of the search tab, include it here.
    ShowSearchController ();
}

public override void ViewDidAppear (bool animated)
{
    base.ViewDidAppear (animated);

    // If the Search Controller is being presented as a modal view,
    // call it here to display it over the contents of the Search
    // tab.
    // ShowSearchController ();
}

アプリが実行され、ユーザーによって選択された [検索] タブでは、フィルター処理されていない項目の完全な一覧がユーザーに表示されます。

既定の検索結果

ユーザーが検索語句の入力を開始すると、結果の一覧がその用語によってフィルター処理され、自動的に更新されます。

フィルター処理された検索結果

ユーザーはいつでも、検索結果の項目にフォーカスを切り替え、Siri Remote の Touch サーフェスをクリックして選択できます。

まとめ

この記事では、Xamarin.tvOS アプリ内のテキスト フィールドと検索フィールドの設計方法と操作方法について説明しました。 インターフェイス デザイナーでテキストと検索コレクションの内容を作成する方法を示し、tvOS でユーザーに検索フィールドを表示する 2 つの異なる方法を示しました。