Xamarin.Mac でストーリーボードを操作
ストーリーボードは、指定のアプリのすべての UI をビュー コントローラーの機能概要に分類し、定義するのに使用します。 Xcode の Interface Builder では、これらの各コントローラーは独自のシーンに配置されます。
ストーリーボードは (.storyboard
の拡張子を持つ) リソース ファイルで、コンパイル時と出荷時には Xamarin.Mac アプリのバンドルに同梱されます。 アプリでのストーリーボードの開始を定義するには、その Info.plist
ファイルを編集し、ドロップダウン ボックスから [メイン インターフェイス] を選択します:
コードから読み込む
コードから指定のストーリーボードを読み込んで、ビュー コントローラーを手動で作成しなければならない場合があります。 以下のコードを使用して次のアクションを実行してください:
// Get new window
var storyboard = NSStoryboard.FromName ("Main", null);
var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;
// Display
controller.ShowWindow(this);
アプリのバンドルに同梱されている、指定のファイル名が付いたストーリーボード ファイルを FromName
で読み込みます。 InstantiateControllerWithIdentifier
で、指定の ID が付いたビュー コントローラーのインスタンスが作成されます。 この ID は、UI の設計時に Xcode の Interface Builder で設定します:
必要に応じて、ビュー コントローラーを読み込むための InstantiateInitialController
メソッドを使用することもできます。ビュー コントローラーは、Interface Builder の初期コントローラーに割り当てられています:
これは、上記のストーリーボード エントリポイントと OpenArrow (open-ended arrow) でマークされています。
ビュー コントローラー
ビュー コントローラーは、Mac アプリ内の情報の指定のビューと、その情報を提供するデータ モデルとの間の関係を定義します。 ストーリーボードの最上位のシーンは、それぞれが Xamarin.Mac アプリのコード内の 1 つのビュー コントローラーを表します。
ビュー コントローラー ライフサイクル
macOS でストーリーボードに対応するための新しいメソッドが NSViewController
クラスにいくつか追加されています。 以下のメソッドは、指定のビュー コントローラーによって制御されるビューのライフサイクルへの応答に使用される最重要のメソッドです:
ViewDidLoad
- このメソッドは、ストーリーボード ファイルからビューが読み込まれる際に呼び出されます。ViewWillAppear
- このメソッドはビューが画面に表示される直前に呼び出されます。ViewDidAppear
- このメソッドはビューが画面に表示された直後に呼び出されます。ViewWillDisappear
- このメソッドはビューが画面から削除される直前に呼び出されます。ViewDidDisappear
- このメソッドはビューが画面から削除された直後に呼び出されます。UpdateViewConstraints
- このメソッドはビューの自動レイアウト位置とサイズを定義する制約を更新する必要がある場合に呼び出されます。ViewWillLayout
- このメソッドは、このビューのサブビューが画面に展開される直前に呼び出されます。ViewDidLayout
- このメソッドは、ビューのサブビューが画面に展開された直後に呼び出されます。
レスポンダー チェーン
NSViewControllers
は、ウィンドウのレスポンダー チェーンの一部でもあります:
そのため、切り取り、コピー、貼り付けなどのメニュー項目の選択のイベントを受信して応答するよう接続されています。 この自動ビュー コントローラーの接続は、macOS Sierra (10.12) 以降で実行されているアプリでのみ行われます。
封じ込め
現在はビュー コントローラー (分割ビュー コントローラーやタブ ビュー コントローラーなど) でコンテインメントを Storyboard に実装し、他のサブ ビュー コントローラーを "包含" できるようになっています:
子ビュー コントローラーには、親ビュー コントローラーに関連付けて、画面からのビューの表示と削除を操作するためのメソッドとプロパティが含まれています。
macOS に組み込まれているすべてのコンテナー ビュー コントローラーには、Apple が推奨する指定のレイアウトがありますので、独自のカスタム コンテナー ビュー コントローラーを作成する際に手本としてください:
コレクション ビュー コントローラーにはコレクション ビュー アイテムの配列が含まれており、アイテムそれぞれに独自のビューを含む 1 つ以上のビュー コントローラーが含まれています。
セグエ
セグエでは、アプリの UI を定義するすべてのシーン間の関係を提供します。 iOS の Storyboard の操作に慣れている場合、iOS 用セグエは通常、全画面表示ビュー間の遷移を定義しているとわかります。 これは macOS とは異なり、セグエでは通常、"コンテインメント" を定義します。ここで、1 つのシーンは親シーンの子です。
macOS では、ほとんどのアプリは、分割ビューやタブなどの UI 要素を使用して、同じウィンドウ内でビューをグループ化する傾向があります。 iOS とは異なり、物理的な表示領域が限られているため、画面のオンとオフを切り替える必要があります。
プレゼンテーション セグエ
macOS が持つ包含の傾向を考えると、モーダル ウィンドウ、シート ビュー、ポップオーバーなど、プレゼンテーション セグエが使用される状況があります。 macOS には、次の組み込みのセグエの種類が用意されています:
- 表示 - セグエのターゲットをモーダルではないウィンドウとして表示します。 たとえばアプリでドキュメント ウィンドウの別のインスタンスを表示するには、この種類のセグエを使用します。
- モーダル - セグエのターゲットをモーダル ウィンドウとして表示します。 たとえばアプリの [基本設定] ウィンドウを表示するには、この種類のセグエを使用します。
- シート - 親ウィンドウへの添付シートとしてセグエのターゲットを表示します。 たとえば [シートの検索と置換] を表示するには、この種類のセグエを使用します。
- ポップオーバー - セグエのターゲットをポップオーバー ウィンドウのように表示します。 たとえば UI 要素をユーザーがクリックするとオプションが表示されるようにするには、この種類のセグエを使用します。
- カスタム - 開発者によって定義された種類のカスタム セグエを使用して、セグエのターゲットを表示します。 詳細については、以下のカスタム セグエの作成セクションを参照してください。
プレゼンテーション セグエを使用する場合は、プレゼンテーションの親ビュー コントローラーの PrepareForSegue
メソッドをオーバーライドして変数を初期化し、表示されているビュー コントローラーにデータを提供できます。
トリガー セグエ
トリガー セグエを使用して、名前付きセグエを (Interface Builder の識別子プロパティを使用して) 指定し、ユーザーがボタンをクリックしたり、コードで PerformSegue
メソッドを呼び出したりするなどのイベントによってトリガーされるようにすることができます:
// Display the Scene defined by the given Segue ID
PerformSegue("MyNamedSegue", this);
セグエ ID は、アプリの UI を展開する際に Xcode の Interface Builder 内で定義されます:
セグエのソースとして機能するビュー コントローラーでは、セグエを実行し、指定のビュー コントローラーを表示させる前に PrepareForSegue
メソッドをオーバーライドして、初期化を行う必要があります:
public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
// Take action based on Segue ID
switch (segue.Identifier) {
case "MyNamedSegue":
// Prepare for the segue to happen
...
break;
}
}
必要に応じて ShouldPerformSegue
メソッドをオーバーライドし、C# コードを使用して実際にセグエを実行するかどうかを制御することもできます。 手動で表示させるビュー コントローラーの場合、不要になった際は DismissController
メソッドを呼び出して表示させないようにします。
カスタムのセグエの作成
アプリでは、macOS で定義されている組み込みセグエによって提供されない種類のセグエが必要になる場合があります。 その場合、アプリの UI を展開する際に Xcode の Interface Builder で割り当てることができるカスタム セグエを作成してください。
たとえば (新しいウィンドウでターゲット シーンを開くのではなく) ウィンドウ内の現在のビュー コントローラーを置き換える新しい種類のセグエを作成するには、次に挙げるコードを使用します:
using System;
using AppKit;
using Foundation;
namespace OnCardMac
{
[Register("ReplaceViewSeque")]
public class ReplaceViewSeque : NSStoryboardSegue
{
#region Constructors
public ReplaceViewSeque() {
}
public ReplaceViewSeque (string identifier, NSObject sourceController, NSObject destinationController) : base(identifier,sourceController,destinationController) {
}
public ReplaceViewSeque (IntPtr handle) : base(handle) {
}
public ReplaceViewSeque (NSObjectFlag x) : base(x) {
}
#endregion
#region Override Methods
public override void Perform ()
{
// Cast the source and destination controllers
var source = SourceController as NSViewController;
var destination = DestinationController as NSViewController;
// Swap the controllers
source.View.Window.ContentViewController = destination;
// Release memory
source.RemoveFromParentViewController ();
}
#endregion
}
}
ここで注意すべき点がいくつかあります:
Register
属性を使用して、このクラスを Objective-C/macOS に公開しています。Perform
メソッドをオーバーライドして、実際にカスタム セグエのアクションを実行しています。- ウィンドウの
ContentViewController
のコントローラーを、セグエのターゲット (宛先) によって定義されたコントローラーに置き換えます。 RemoveFromParentViewController
メソッドを使用してメモリを解放するには、元のビュー コントローラーを削除します。
Xcode の Interface Builder でこの新しい種類のセグエを使用するには、まずアプリをコンパイルしてから Xcode に切り替えて、2 つのシーン間に新しいセグエを追加する必要があります。 [スタイル] を [カスタム] に、[セグエのクラス] を ReplaceViewSegue
(カスタム セグエのクラスの名前) にそれぞれ設定します:
ウィンドウ コントローラー
ウィンドウ コントローラーには macOS アプリで作成できるさまざまな種類のウィンドウが含まれており、ウィンドウ コントローラーでそれらのウィンドウを制御できます。 Storyboard の場合、次の機能があります:
- コンテンツ ビュー コントローラーが必ず付属されています。 このコンテンツ ビュー コントローラーは、子ウィンドウにあるものと同じです。
Storyboard
プロパティで、ウィンドウ コントローラーが読み込まれたストーリーボードが含まれます。それ以外のプロパティnull
の場合、ウィンドウ コントローラーはストーリーボードから読み込まれません。DismissController
メソッドを呼び出し、指定のウィンドウを閉じてビューから削除します。
ビュー コントローラーと同様に、ウィンドウ コントローラーに PerformSegue
、PrepareForSegue
、ShouldPerformSegue
メソッドを実装すると、セグエ操作のソースとして使用できるようになります。
ウィンドウ コントローラーは macOS アプリの次の機能に関与します:
- 特定のウィンドウの管理。
- ウィンドウのタイトル バーとツール バー (使用可能な場合) の管理。
- コンテンツ ビュー コントローラーを管理して、ウィンドウのコンテンツを表示します。
ジェスチャ認識エンジン
macOS 用のジェスチャ認識エンジンは iOS で対応するジェスチャとほぼ同じであり、開発者はジェスチャ (マウス ボタンのクリックなど) をアプリの UI 内の要素に簡単に追加できます。
しかし、iOS でのジェスチャはアプリの設計 (2 本の指で画面をタップするなど) によって決定し、macOS でのほとんどのジェスチャはハードウェアによって決定するという違いがあります。
ジェスチャ認識エンジンを使用することで、UI 項目にカスタム操作を追加するのに必要なコードの量を大幅に減少させることができます。 ダブル クリック、シングル クリック、イベントをクリックしてドラッグなどのカスタム操作を自動で決定できます。
MouseDown
イベントをビュー コントローラーでオーバーライドする代わりに、Storyboard を操作する際にジェスチャ認識エンジンを使用して、ユーザー入力イベントを処理することをお勧めします。
macOS では次のジェスチャ認識エンジンを使用できます:
NSClickGestureRecognizer
- マウスの上下のイベントの登録。NSPanGestureRecognizer
- マウス ボタンを下に登録し、イベントをドラッグして解放。NSPressGestureRecognizer
- 指定の時間イベントに対してマウス ボタンを押したまま登録。NSMagnificationGestureRecognizer
- トラックパッド ハードウェアから拡大イベントの登録。NSRotationGestureRecognizer
- トラックパッド ハードウェアから回転イベントの登録。
ストーリーボード参照を使用
ストーリーボード参照を使用することで、大規模で複雑なストーリーボードの設計を採用したり、元のストーリーボードから参照されたより小規模のストーリーボードに分割したりできるようになります。これにより複雑さは解消され、結果的に個々のストーリーボードの設計と保守が容易になります。
またストーリーボード参照を使用することで、同じストーリーボード内の別のシーン、または別のストーリーボード上の指定のシーンにアンカーを提供できるようになります。
外部ストーリーボードを参照
外部ストーリーボードへの参照を追加するには、次の操作を行います:
ソリューション エクスプローラーで [プロジェクト名] を右クリックし、[追加] > [新しいファイル] > [Mac] > [ストーリーボード] の順に選択します。 新しいストーリーボードの名前を入力し、[新規] ボタンをクリックします:
ソリューション エクスプローラーで、新しいストーリーボード名をダブル クリックしてストーリーボードを開き、Xcode の Interface Builder で編集します。
通常と同じように、新しいストーリーボードのシーンのレイアウトを設計し、変更を保存します:
Interface Builder で、参照を追加するストーリーボードに切り替えます。
[ストーリーボード参照] を [オブジェクト ライブラリ] から [デザイン サーフェイス] にドラッグします:
[属性インスペクター] で、上で作成したストーリーボードの名前を選択します:
既存のシーン上の UI ウィジェット (ボタンなど) を Control キーを押しながらクリックし、先ほど作成した [ストーリーボード参照] に新しいセグエを作成します。 ポップアップ メニューから [表示] を選択して、セグエの作成を完了します:
ストーリーボードに変更を保存します。
Visual Studio for Mac に戻って変更を同期します。
アプリが実行され、セグエを作成するのに使用した UI 要素をユーザーがクリックすると、ストーリーボード参照で指定された外部ストーリーボードからの初期ウィンドウ コントローラーが表示されます。
外部ストーリーボードでの特定シーンへの参照
(初期ウィンドウ コントローラーではなく) 外部ストーリーボードに特定シーンへの参照を追加するには、次の操作を行います:
ソリューション エクスプローラーで、外部ストーリーボードをダブル クリックして開き、Xcode の Interface Builder で編集します。
新しいシーンを追加し、通常と同じようにそのレイアウトを設計します:
[ID インスペクター] に、新しいシーンのウィンドウ コントローラーのストーリーボード ID を入力します:
Interface Builder で参照を追加するストーリーボードを開きます。
[ストーリーボード参照] を [オブジェクト ライブラリ] から [デザイン サーフェイス] にドラッグします:
[ID インスペクター] で、ストーリーボードの名前と、上で作成したシーンの参照 ID (ストーリーボード ID) を選択します:
既存のシーン上の UI ウィジェット (ボタンなど) を Control キーを押しながらクリックし、先ほど作成した [ストーリーボード参照] に新しいセグエを作成します。 ポップアップ メニューから [表示] を選択して、セグエの作成を完了します:
ストーリーボードに変更を保存します。
Visual Studio for Mac に戻って変更を同期します。
アプリが実行され、セグエを作成するのに使用した UI 要素をユーザーがクリックすると、ストーリーボード参照で特定された外部ストーリーボードからの指定のストーリーボード ID が付いたシーンが表示されます。
同じストーリーボード内の特定シーンの参照
特定シーンへの参照を同じストーリーボードに追加するには、次の操作を行います:
ソリューション エクスプローラーで、ストーリーボードをダブル クリックして開き、編集します。
新しいシーンを追加し、通常と同じようにそのレイアウトを設計します:
[ID インスペクター] に、新しいシーンのウィンドウ コントローラーのストーリーボード ID を入力します:
ストーリーボード参照を [ツールボックス] から [デザイン サーフェイス] にドラッグします:
[属性インスペクター] で、上で作成したシーンの参照 ID (ストーリーボード ID) を選択します:
既存のシーン上の UI ウィジェット (ボタンなど) を Control キーを押しながらクリックし、先ほど作成した [ストーリーボード参照] に新しいセグエを作成します。 ポップアップ メニューから [表示] を選択して、セグエの作成を完了します:
ストーリーボードに変更を保存します。
Visual Studio for Mac に戻って変更を同期します。
アプリが実行され、セグエを作成するのに使用した UI 要素をユーザーがクリックすると、ストーリーボード参照で特定された同じストーリーボードでの指定のストーリーボード ID が付いたシーンが表示されます。