Xamarin での tvOS コレクション ビューの操作

コレクション ビューを使用すると、任意のレイアウトを使用してコンテンツのグループを表示できます。 組み込みのサポートを使用すると、グリッド状または線形のレイアウトを簡単に作成でき、カスタム レイアウトもサポートされます。

[コレクション] ビューのサンプル

コレクション ビューでは、デリゲートとデータ ソースの両方を使用して項目のコレクションを保持して、ユーザー操作とコレクションのコンテンツを提供します。 コレクション ビューは、ビュー自体から独立したレイアウト サブシステムに基づいているため、別のレイアウトを指定すると、コレクション ビューのデータの表示をその場で簡単に変更できます。

コレクション ビューについて

前述のとおり、コレクション ビュー (UICollectionView) は、項目の順序付けられたコレクションを管理し、それらの項目をカスタマイズ可能なレイアウトで表示します。 コレクション ビューは、レイアウトを使用して項目を 1 つ以上の列に表示できる点を除いて、テーブル ビュー (UITableView) と同様に機能します。

tvOS でコレクション ビューを使用する場合、アプリは、データ ソース (UICollectionViewDataSource) を使用して、コレクションに関連付けられているデータを提供する役割を担います。 コレクション ビューのデータは、必要に応じて、別のグループ (セクション) に編成して表示できます。

コレクション ビューでは、セル (UICollectionViewCell) を使用して個々の項目を画面上に表示します。セルには、コレクションから得た特定の情報 (画像やそのタイトルなど) が表示されます。

必要に応じて、セクションとセルのヘッダーおよびフッターとして機能する補助ビューをコレクション ビューの表示に追加できます。 コレクション ビューのレイアウトは、個々のセルに加えて補助ビューの配置も定義する役割を担います。

コレクション ビューでは、デリゲート (UICollectionViewDelegate) を使用してユーザー操作に応答できます。 このデリゲートは、セルが強調表示されている場合、またはセルが選択されている場合に、そのセルがフォーカスを取得できるかどうかを決定する役割も担います。 場合によっては、デリゲートによって個々のセルのサイズが決まることもあります。

コレクション ビュー レイアウト

コレクション ビューの主な機能は、表示するデータとそのレイアウトの分離です。 コレクション ビュー レイアウト (UICollectionViewLayout) は、コレクション ビューの画面上の表示でセル (および補助ビュー) の編成と場所を指定する役割を担います。

個々のセルは、コレクション ビューによって、それに関連付けられているデータ ソースから作成され、指定されたコレクション ビュー レイアウトによって配置され、表示されます。

コレクション ビュー レイアウトは、通常、コレクション ビューの作成時に提供されます。 ただし、コレクション ビュー レイアウトはいつでも変更でき、コレクション ビューのデータの画面上の表示は、提供された新しいレイアウトを使用して自動的に更新されます。

コレクション ビュー レイアウトには、2 つの異なるレイアウト間の切り替えをアニメーション化するために使用できるいくつかの方法が用意されています (既定では、アニメーション化は行われません)。 さらに、コレクション ビュー レイアウトは、ジェスチャー認識エンジンと連携して、レイアウトの変更が発生するユーザー操作をアニメーション化することもできます。

セルと補助ビューの作成

コレクション ビューのデータ ソースは、コレクション項目を裏付けるデータを提供するだけでなく、コンテンツの表示に使用されるセルを提供する役割も担います。

コレクション ビューは、項目の大規模なコレクションを処理するように設計されているため、メモリ制限の超過を防ぐために、個々のセルをデキューして再利用することができます。 ビューをデキューするには、2 つの異なるメソッドがあります。

  • DequeueReusableCell - 指定された種類 (アプリのストーリーボードで指定) のセルを作成するか、返します。
  • DequeueReusableSupplementaryView - 指定された種類 (アプリのストーリーボードで指定) の補助ビューを作成するか、返します。

これらのメソッドのいずれかを呼び出す前に、セルのビューを作成するために使用するクラス、ストーリーボード、または .xib ファイルをコレクション ビューに登録する必要があります。 次に例を示します。

public CityCollectionView (IntPtr handle) : base (handle)
{
    // Initialize
    RegisterClassForCell (typeof(CityCollectionViewCell), CityViewDatasource.CardCellId);
    ...
}

ここで、typeof(CityCollectionViewCell) は、ビューをサポートするクラスを提供し、CityViewDatasource.CardCellId は、セル (またはビュー) がデキューされるときに使用する ID を提供します。

セルがデキューされた後、そのセルに表示されている項目のデータを使用してセルを構成し、コレクション ビューに戻って表示します。

コレクション ビュー コントローラーについて

コレクション ビュー コントローラー (UICollectionViewController) は、次の動作を提供する特殊なビュー コントローラー (UIViewController) です。

  • ストーリーボードまたは .xib ファイルからコレクション ビューを読み込み、ビューのインスタンスを作成する責任を担います。 コード内で作成されると、構成されていない新しいコレクション ビューを自動的に作成します。
  • コレクション ビューが読み込まれると、コントローラーは、コレクション ビューのデータ ソースとデリゲートをストーリーボードまたは .xib ファイルから読み込もうとします。 使用できるものがない場合、コントローラーは、それ自体を両方のソースとして設定します。
  • 最初の表示で、コレクション ビューが設定される前にデータが確実に読み込まれるようにし、後続の各表示で、選択を再度読み込んでクリアします。

さらに、コレクション ビュー コントローラーには、AwakeFromNibViewWillDisplay など、コレクション ビューのライフサイクルを管理するために使用できるオーバーライド可能なメソッドも用意されています。

コレクション ビューとストーリーボード

Xamarin.tvOS アプリでコレクション ビューを操作する最も簡単な方法は、アプリのストーリーボードにコレクション ビューを追加することです。 簡単な例として、画像、タイトル、選択ボタンを表示するサンプル アプリを作成します。 ユーザーが選択ボタンをクリックすると、コレクション ビューが表示され、ユーザーは新しい画像を選択できます。 画像が選択されると、コレクション ビューは閉じられ、新しい画像とタイトルが表示されます。

次の操作を行います。

  1. Visual Studio for Mac で、新しいシングル ビュー tvOS アプリを起動します。

  2. ソリューション エクスプローラーで、Main.storyboard ファイルをダブルクリックし、それを iOS Designer で開きます。

  3. 画像ビュー、ラベル、ボタンを既存のビューに追加し、それらを次のように構成します。

    サンプル レイアウト

  4. プロパティ エクスプローラー[ウィジェット] タブで、画像ビューとラベルに [名前] を割り当てます。 次に例を示します。

    名前の設定

  5. 次に、コレクション ビュー コントローラーをストーリーボードにドラッグします。

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

  6. ボタンからコレクション ビュー コントローラーに、コントロール キーを押しながらドラッグし、ポップアップから [プッシュ] を選択します。

    ポップアップから [プッシュ] を選択する

  7. このアプリを実行すると、ユーザーがボタンをクリックするたびにコレクション ビューが表示されます。

  8. コレクション ビューを選択し、プロパティ エクスプローラー[レイアウト] タブに次の値を入力します。

    プロパティ エクスプローラー

  9. これにより、個々のセルのサイズと、セルとコレクション ビューの外側の端との間の境界線が制御されます。

  10. コレクション ビュー コントローラーを選択し、[ウィジェット] タブで、そのクラスを CityCollectionViewController に設定します。

    クラスを CityCollectionViewController に設定する

  11. コレクション ビューを選択し、[ウィジェット] タブで、そのクラスを CityCollectionView に設定します。

    クラスを CityCollectionView に設定する

  12. コレクション ビューのセルを選択し、[ウィジェット] タブで、そのクラスを CityCollectionViewCell に設定します。

    クラスを CityCollectionViewCell に設定する

  13. [ウィジェット] タブで、コントロール ビューの [レイアウト]Flow で、[スクロール方向]Vertical であることを確認します。

    [ウィジェット] タブ

  14. コレクション ビューのセルを選択し、[ウィジェット] タブで、その[ID]CityCell に設定します。

    ID を CityCell に設定する

  15. 変更を保存。

コレクション ビューの [レイアウト] として Custom を選択した場合、カスタム レイアウトを指定できます。 Apple は、データをグリッドベースのレイアウトで簡単に表示できる組み込みの UICollectionViewFlowLayoutUICollectionViewDelegateFlowLayout を提供しています (これらは、flow レイアウト スタイルで使用されます)。

ストーリーボードの使用の詳細については、「はじめての tvOS クイック スタート ガイド」を参照してください。

コレクション ビューのデータの提供

コレクション ビュー (およびコレクション ビュー コントローラー) をストーリーボードに追加したので、コレクションのデータを提供する必要があります。

データ モデル

まず、データのモデルを作成します。このモデルには、表示する画像のファイル名、タイトル、都市を選択できるようにするためのフラグを保持します。

CityInfo クラスを作成し、次のような内容にします。

using System;

namespace tvCollection
{
    public class CityInfo
    {
        #region Computed Properties
        public string ImageFilename { get; set; }
        public string Title { get; set; }
        public bool CanSelect{ get; set; }
        #endregion

        #region Constructors
        public CityInfo (string filename, string title, bool canSelect)
        {
            // Initialize
            this.ImageFilename = filename;
            this.Title = title;
            this.CanSelect = canSelect;
        }
        #endregion
    }
}

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

次に、各セルのデータの表示方法を定義する必要があります。 CityCollectionViewCell.cs ファイル (ストーリーボード ファイルから自動的に作成されます) を編集し、次のような内容にします。

using System;
using Foundation;
using UIKit;
using CoreGraphics;

namespace tvCollection
{
    public partial class CityCollectionViewCell : UICollectionViewCell
    {
        #region Private Variables
        private CityInfo _city;
        #endregion

        #region Computed Properties
        public UIImageView CityView { get ; set; }
        public UILabel CityTitle { get; set; }

        public CityInfo City {
            get { return _city; }
            set {
                _city = value;
                CityView.Image = UIImage.FromFile (City.ImageFilename);
                CityView.Alpha = (City.CanSelect) ? 1.0f : 0.5f;
                CityTitle.Text = City.Title;
            }
        }
        #endregion

        #region Constructors
        public CityCollectionViewCell (IntPtr handle) : base (handle)
        {
            // Initialize
            CityView = new UIImageView(new CGRect(22, 19, 320, 171));
            CityView.AdjustsImageWhenAncestorFocused = true;
            AddSubview (CityView);

            CityTitle = new UILabel (new CGRect (22, 209, 320, 21)) {
                TextAlignment = UITextAlignment.Center,
                TextColor = UIColor.White,
                Alpha = 0.0f
            };
            AddSubview (CityTitle);
        }
        #endregion


    }
}

この tvOS アプリでは、画像とオプションのタイトルを表示します。 特定の都市を選択できない場合は、次のコードを使用して画像ビューを淡色表示します。

CityView.Alpha = (City.CanSelect) ? 1.0f : 0.5f;

ユーザーによって画像を含むセルにフォーカスが設定された場合、次のプロパティを設定して、そのセルに対して組み込みの視差効果を使用します。

CityView.AdjustsImageWhenAncestorFocused = true;

ナビゲーションとフォーカスの詳細については、ナビゲーションとフォーカスの操作およびSiri リモートコントローラーと Bluetooth コントローラーに関するドキュメントを参照してください。

コレクション ビューのデータ プロバイダー

データ モデルを作成し、セル レイアウトを定義したら、コレクション ビューのデータ ソースを作成しましょう。 データ ソースは、裏付けデータを提供するだけでなく、セルをデキューして個々のセルを画面上に表示する役割も担います。

CityViewDatasource クラスを作成し、次のような内容にします。

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

namespace tvCollection
{
    public class CityViewDatasource : UICollectionViewDataSource
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Static Constants
        public static NSString CardCellId = new NSString ("CityCell");
        #endregion

        #region Computed Properties
        public List<CityInfo> Cities { get; set; } = new List<CityInfo>();
        public CityCollectionView ViewController { get; set; }
        #endregion

        #region Constructors
        public CityViewDatasource (CityCollectionView controller)
        {
            // Initialize
            this.ViewController = controller;
            PopulateCities ();
        }
        #endregion

        #region Public Methods
        public void PopulateCities() {

            // Clear existing cities
            Cities.Clear();

            // Add new cities
            Cities.Add(new CityInfo("City01.jpg", "Houses by Water", false));
            Cities.Add(new CityInfo("City02.jpg", "Turning Circle", true));
            Cities.Add(new CityInfo("City03.jpg", "Skyline at Night", true));
            Cities.Add(new CityInfo("City04.jpg", "Golden Gate Bridge", true));
            Cities.Add(new CityInfo("City05.jpg", "Roads by Night", true));
            Cities.Add(new CityInfo("City06.jpg", "Church Domes", true));
            Cities.Add(new CityInfo("City07.jpg", "Mountain Lights", true));
            Cities.Add(new CityInfo("City08.jpg", "City Scene", false));
            Cities.Add(new CityInfo("City09.jpg", "House in Winter", true));
            Cities.Add(new CityInfo("City10.jpg", "By the Lake", true));
            Cities.Add(new CityInfo("City11.jpg", "At the Dome", true));
            Cities.Add(new CityInfo("City12.jpg", "Cityscape", true));
            Cities.Add(new CityInfo("City13.jpg", "Model City", true));
            Cities.Add(new CityInfo("City14.jpg", "Taxi, Taxi!", true));
            Cities.Add(new CityInfo("City15.jpg", "On the Sidewalk", true));
            Cities.Add(new CityInfo("City16.jpg", "Midnight Walk", true));
            Cities.Add(new CityInfo("City17.jpg", "Lunchtime Cafe", true));
            Cities.Add(new CityInfo("City18.jpg", "Coffee Shop", true));
            Cities.Add(new CityInfo("City19.jpg", "Rustic Tavern", true));
        }
        #endregion

        #region Override Methods
        public override nint NumberOfSections (UICollectionView collectionView)
        {
            return 1;
        }

        public override nint GetItemsCount (UICollectionView collectionView, nint section)
        {
            return Cities.Count;
        }

        public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
        {
            var cityCell = (CityCollectionViewCell)collectionView.DequeueReusableCell (CardCellId, indexPath);
            var city = Cities [indexPath.Row];

            // Initialize city
            cityCell.City = city;

            return cityCell;
        }
        #endregion
    }
}

このクラスを詳しく見てみましょう。 まず、UICollectionViewDataSource から継承し、(iOS Designer で割り当てた) セル ID へのショートカットを提供します。

public static NSString CardCellId = new NSString ("CityCell");

次に、コレクション データのストレージを提供し、データを設定するクラスを提供します。

public List<CityInfo> Cities { get; set; } = new List<CityInfo>();
...

public void PopulateCities() {

    // Clear existing cities
    Cities.Clear();

    // Add new cities
    Cities.Add(new CityInfo("City01.jpg", "Houses by Water", false));
    Cities.Add(new CityInfo("City02.jpg", "Turning Circle", true));
    ...
}

その後、NumberOfSections メソッドをオーバーライドし、コレクション ビューに含まれるセクション (項目のグループ) の数を返します。 この場合は、1 つしかありません。

public override nint NumberOfSections (UICollectionView collectionView)
{
    return 1;
}

次に、次のコードを使用して、コレクション内の項目数を返します。

public override nint GetItemsCount (UICollectionView collectionView, nint section)
{
    return Cities.Count;
}

最後に、次のコードを使用して、コレクション ビューが要求したときに再利用可能なセルをデキューします。

public override UICollectionViewCell GetCell (UICollectionView collectionView, NSIndexPath indexPath)
{
    var cityCell = (CityCollectionViewCell)collectionView.DequeueReusableCell (CardCellId, indexPath);
    var city = Cities [indexPath.Row];

    // Initialize city
    cityCell.City = city;

    return cityCell;
}

CityCollectionViewCell 型のコレクション ビューのセルを取得したら、指定された項目をそのセルに設定します。

ユーザー イベントへの応答

ユーザーがコレクションから項目を選択できるようにする必要があるため、この操作を処理するコレクション ビューのデリゲートを提供する必要があります。 また、ユーザーが選択した項目を呼び出し元のビューに通知する方法を提供する必要もあります。

アプリ デリゲート

現在選択されている項目をコレクション ビューから呼び出し元のビューに関連付ける方法が必要です。 AppDelegate でカスタム プロパティを使用します。 AppDelegate.cs ファイルを編集し、次のコードを追加します。

public CityInfo SelectedCity { get; set;} = new CityInfo("City02.jpg", "Turning Circle", true);

これにより、プロパティが定義され、最初に表示される既定の都市が設定されます。 後でこのプロパティを使用して、ユーザーの選択を表示し、選択を変更できるようにします。

コレクション ビューのデリゲート

次に、新しい CityViewDelegate クラスをプロジェクトに追加し、次のような内容にします。

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

namespace tvCollection
{
    public class CityViewDelegate : UICollectionViewDelegateFlowLayout
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Constructors
        public CityViewDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
        {
            return new CGSize (361, 256);
        }

        public override bool CanFocusItem (UICollectionView collectionView, NSIndexPath indexPath)
        {
            if (indexPath == null) {
                return false;
            } else {
                var controller = collectionView as CityCollectionView;
                return controller.Source.Cities[indexPath.Row].CanSelect;
            }
        }

        public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
        {
            var controller = collectionView as CityCollectionView;
            App.SelectedCity = controller.Source.Cities [indexPath.Row];

            // Close Collection
            controller.ParentController.DismissViewController(true,null);
        }
        #endregion
    }
}

このクラスを詳しく見てみましょう。 まず、UICollectionViewDelegateFlowLayout から継承します。 UICollectionViewDelegate ではなくこのクラスを継承するのは、カスタム レイアウトの種類ではなく組み込みの UICollectionViewFlowLayout を使用して項目を表示するためです。

次に、次のコードを使用して、個々の項目のサイズを返します。

public override CGSize GetSizeForItem (UICollectionView collectionView, UICollectionViewLayout layout, NSIndexPath indexPath)
{
    return new CGSize (361, 256);
}

その後、次のコードを使用して、特定のセルがフォーカスを取得できるかどうかを決定します。

public override bool CanFocusItem (UICollectionView collectionView, NSIndexPath indexPath)
{
    if (indexPath == null) {
        return false;
    } else {
        var controller = collectionView as CityCollectionView;
        return controller.Source.Cities[indexPath.Row].CanSelect;
    }
}

裏付けデータの特定の部分の CanSelect フラグが true に設定されているかどうかを確認し、その値を返します。 ナビゲーションとフォーカスの詳細については、ナビゲーションとフォーカスの操作およびSiri リモートコントローラーと Bluetooth コントローラーに関するドキュメントを参照してください。

最後に、次のコードを使用して、項目を選択したユーザーに応答します。

public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
{
    var controller = collectionView as CityCollectionView;
    App.SelectedCity = controller.Source.Cities [indexPath.Row];

    // Close Collection
    controller.ParentController.DismissViewController(true,null);
}

ここで、AppDelegateSelectedCity プロパティを、ユーザーが選択した項目に設定し、コレクション ビュー コントローラーを閉じ、呼び出し元のビューに戻ります。 コレクション ビューの ParentController プロパティをまだ定義していないので、次に、それを定義します。

コレクション ビューの構成

次に、コレクション ビューを編集し、データ ソースとデリゲートを割り当てる必要があります。 CityCollectionView.cs ファイル (ストーリーボードから自動的に作成されます) を編集し、次のような内容にします。

using System;
using Foundation;
using UIKit;

namespace tvCollection
{
    public partial class CityCollectionView : UICollectionView
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Computed Properties
        public CityViewDatasource Source {
            get { return DataSource as CityViewDatasource;}
        }

        public CityCollectionViewController ParentController { get; set;}
        #endregion

        #region Constructors
        public CityCollectionView (IntPtr handle) : base (handle)
        {
            // Initialize
            RegisterClassForCell (typeof(CityCollectionViewCell), CityViewDatasource.CardCellId);
            DataSource = new CityViewDatasource (this);
            Delegate = new CityViewDelegate ();
        }
        #endregion

        #region Override Methods
        public override nint NumberOfSections ()
        {
            return 1;
        }

        public override void DidUpdateFocus (UIFocusUpdateContext context, UIFocusAnimationCoordinator coordinator)
        {
            var previousItem = context.PreviouslyFocusedView as CityCollectionViewCell;
            if (previousItem != null) {
                Animate (0.2, () => {
                    previousItem.CityTitle.Alpha = 0.0f;
                });
            }

            var nextItem = context.NextFocusedView as CityCollectionViewCell;
            if (nextItem != null) {
                Animate (0.2, () => {
                    nextItem.CityTitle.Alpha = 1.0f;
                });
            }
        }
        #endregion
    }
}

まず、AppDelegate にアクセスするためのショートカットを提供します。

public static AppDelegate App {
    get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}

次に、コレクション ビューのデータ ソースへのショートカットと、コレクション ビュー コントローラーにアクセスするためのプロパティを提供します (ユーザーが選択したときにコレクションを閉じるために上記のデリゲートによって使用されます)。

public CityViewDatasource Source {
    get { return DataSource as CityViewDatasource;}
}

public CityCollectionViewController ParentController { get; set;}

その後、次のコードを使用して、コレクション ビューを初期化し、セルのクラス、データ ソース、デリゲートを割り当てます。

public CityCollectionView (IntPtr handle) : base (handle)
{
    // Initialize
    RegisterClassForCell (typeof(CityCollectionViewCell), CityViewDatasource.CardCellId);
    DataSource = new CityViewDatasource (this);
    Delegate = new CityViewDelegate ();
}

最後に、画像の下のタイトルは、ユーザーが画像を強調表示した (フォーカスを設定した) 場合にのみ表示されるようにします。 これを行うには、次のコードを使用します。

public override void DidUpdateFocus (UIFocusUpdateContext context, UIFocusAnimationCoordinator coordinator)
{
    var previousItem = context.PreviouslyFocusedView as CityCollectionViewCell;
    if (previousItem != null) {
        Animate (0.2, () => {
            previousItem.CityTitle.Alpha = 0.0f;
        });
    }

    var nextItem = context.NextFocusedView as CityCollectionViewCell;
    if (nextItem != null) {
        Animate (0.2, () => {
            nextItem.CityTitle.Alpha = 1.0f;
        });
    }
}

フォーカスを失った前の項目の透明度をゼロ (0) に設定し、フォーカスを取得した次の項目の透明度を 100% に設定します。 これらの切り替えもアニメーション化されます。

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

次に、コレクション ビューで最後の構成を行い、ユーザーが選択した後にコレクション ビューを閉じることができるように、定義したプロパティをコントローラーが設定できるようにする必要があります。

CityCollectionViewController.cs ファイル (ストーリーボードから自動的に作成されます) を編集し、次のような内容にします。

// This file has been autogenerated from a class added in the UI designer.

using System;

using Foundation;
using UIKit;

namespace tvCollection
{
    public partial class CityCollectionViewController : UICollectionViewController
    {
        #region Computed Properties
        public CityCollectionView Collection {
            get { return CollectionView as CityCollectionView; }
        }
        #endregion

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

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

            // Save link to controller
            Collection.ParentController = this;
        }
        #endregion
    }
}

まとめ

コレクション ビューにデータを入力して制御するためのすべての部分を作成したので、最後にメイン ビューを編集して、すべてをまとめる必要があります。

ViewController.cs ファイル (ストーリーボードから自動的に作成されます) を編集し、次のような内容にします。

using System;
using Foundation;
using UIKit;
using tvCollection;

namespace MySingleView
{
    public partial class ViewController : UIViewController
    {
        #region Application Access
        public static AppDelegate App {
            get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
        }
        #endregion

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

        #region Override Methods
        public override void ViewDidLoad ()
        {
            base.ViewDidLoad ();
            // Perform any additional setup after loading the view, typically from a nib.
        }

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

            // Update image with the currently selected one
            CityView.Image = UIImage.FromFile(App.SelectedCity.ImageFilename);
            BackgroundView.Image = CityView.Image;
            CityTitle.Text = App.SelectedCity.Title;
        }

        public override void DidReceiveMemoryWarning ()
        {
            base.DidReceiveMemoryWarning ();
            // Release any cached data, images, etc that aren't in use.
        }
        #endregion
    }
}

次のコードは、最初に AppDelegateSelectedCity プロパティから選択された項目を表示し、ユーザーがコレクション ビューから選択したときにそれを再表示します。

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

    // Update image with the currently selected one
    CityView.Image = UIImage.FromFile(App.SelectedCity.ImageFilename);
    BackgroundView.Image = CityView.Image;
    CityTitle.Text = App.SelectedCity.Title;
}

アプリのテスト

すべての準備が整った状態で、アプリをビルドして実行すると、既定の都市を表示したメイン ビューが表示されます。

メイン画面

ユーザーが [ビューの選択] ボタンをクリックすると、コレクション ビューが表示されます。

[コレクション] ビュー

CanSelect プロパティが false に設定されている都市は淡色表示され、ユーザーは、その都市にフォーカスを設定できません。 ユーザーが項目を強調表示する (フォーカスを設定する) と、タイトルが表示され、ユーザーは、視差効果を使用して、画像を 3D で微妙に傾けることができます。

ユーザーが選択した画像をクリックすると、コレクション ビューが閉じられ、新しい画像を表示したメイン ビューが再表示されます。

ホーム画面の新しい画像

カスタム レイアウトの作成と項目の並べ替え

コレクション ビューを使用する主な機能の 1 つは、カスタム レイアウトを作成する機能です。 tvOS は iOS から継承されるため、カスタム レイアウトを作成するプロセスは同じです。 詳細については、コレクション ビューの概要のドキュメントを参照してください。

最近、iOS 9 のコレクション ビューに、コレクション内の項目を簡単に並べ替える機能が追加されました。 tvOS 9 は iOS 9 のサブセットであるため、この並べ替えも同じ方法で行われます。 詳細については、コレクション ビューの変更に関するドキュメントを参照してください。

まとめ

この記事では、Xamarin.tvOS アプリ内でのコレクション ビューの設計と操作について説明しました。 最初に、コレクション ビューを構成するすべての要素について説明しました。 次に、ストーリーボードを使用して、コレクション ビューを設計および実装する方法について説明しました。 最後に、カスタム レイアウトの作成と項目の並べ替えに関する情報へのリンクを提供します。