Xamarin.iOS での .xib コード生成

Apple Interface Builder ツール ("IB") を使用すると、ユーザー インターフェイスを視覚的に設計できます。 IB によって作成されたインターフェイス定義は .xib ファイルに保存されます。 .xib ファイル内のウィジェットやその他のオブジェクトには、カスタム ユーザー定義型の "クラス ID" を指定できます。 カスタム型を使用すると、ウィジェットの動作をカスタマイズしたり、カスタム ウィジェットを記述したりできます。

通常、これらのユーザー クラスは UI コントローラー クラスのサブクラスです。 これには、インターフェイス オブジェクトに 接続できる "アウトレット"s (プロパティ) と "アクション" (イベント) があります。 実行時に、IB が読み込まれます。 その時点で、オブジェクトが作成され、アウトレットとアクションがさまざまな UI オブジェクトに動的に接続されます。 これらのマネージド クラスを定義する場合は、すべてのアウトレットとアクションを、IB で想定されているものと一致するよう定義する必要があります。 Visual Studio for Mac では、CodeBehind に類似したモデルを使用してコードを簡略化します。 Xcode にも同様の Objective-C モデルがあります。 ただし、Xamarin.iOS コード生成モデルと規則は、.NET 開発者にとってより使い慣れたものとなるよう調整されています。

.xib ファイルとカスタム クラス

Cocoa Touch の既存の型を使用するだけでなく、.xib ファイルでカスタム型を定義できます。 他の .xib ファイルで定義されている型や、純粋に C# コードで定義されている型を使用することもできます。 現在、Interface Builder では、現在の .xib ファイルの外部で定義されている型の詳細を認識しないため、それらを一覧表示したり、カスタム アウトレットとアクションを表示したりすることはありません。 将来的に、この制限は解除される予定です。

カスタム クラスは、Interface Builder の [クラス] タブの [サブクラスの追加] コマンドを使用して、.xib ファイルで定義できます。 これらのクラスを "CodeBehind" クラスと呼びます。 プロジェクト内に、.xib ファイルに対応する ".xib.designer.cs" ファイルがある場合、Visual Studio for Mac では、.xib 内のすべてのカスタム クラスの部分クラス定義が自動的に入力されます。 これらの部分クラスを "デザイナー クラス" と呼びます。

コードの生成

コード生成は、{0}.xib.designer.cs ファイルが存在することで有効になります。このファイルは、Page のビルド アクションを伴う任意の {0}.xib ファイルに対応するものです。 Visual Studio for Mac では、.xib ファイルで見つかるすべてのユーザー クラスについて、部分クラスがデザイナー ファイルに生成されます。 Visual Studio for Mac によって、アウトレットとアクションの部分メソッドのプロパティが生成されます。

.xib ファイルが変更され、Visual Studio for Mac にフォーカスが戻されると、デザイナー ファイルは自動的に更新されます。 デザイナー ファイルに変更を加えるのはお勧めしません。Visual Studio for Mac が次回ファイルを更新するときに変更は上書きされます。

登録と名前空間

Visual Studio for Mac では、デザイナー ファイルの場所についてのプロジェクトの既定の名前空間を使用してデザイナー クラスが生成されます。 この動作は、通常の .NET プロジェクト名前空間の生成と一貫したものです。 デザイナー ファイルの名前空間では、プロジェクトの "既定の名前空間" とその ".NET 名前付けポリシー" 設定が使用されます。 プロジェクトの既定の名前空間が変更されると、再生成されたクラスは新しい名前空間を使用します。 再生成後、部分クラスが一致しなくなっている場合があります。

Objective-C ランタイムによってクラスを検出できるようにするため、Visual Studio for Mac ではクラスに [Register (name)] 属性が適用されます。 Xamarin.iOS では NSObject から派生されたクラスの登録が自動的に行われますが、完全修飾 .NET 名が使用されます。 Visual Studio for Mac によって適用される属性は、Xamarin.iOS の動作をオーバーライドして、各クラスが .xib ファイルで使用される名前で登録されるようにします。 IB を使用して定義されたすべてのカスタム クラスに対して手動で属性を追加します。Visual Studio for Mac を使用したデザイナー ファイルの生成は行いません。 そうすることで、マネージド クラスが想定される Objective-C クラス名と一致するようになります。

クラスを複数の .xib で定義することはできません。競合が発生します。

非デザイナー クラスのパーツ

デザイナーの部分クラスは、そのまま使用することを意図していません。 アウトレットはプライベートであり、基底クラスが指定されていません。 各クラスには、対応する "非デザイナー" クラスのパーツが、別のファイルに含まれていると想定されます。 "非デザイナー" ファイルは、基底クラスを設定し、アウトレットを操作し、ネイティブ コードからクラスをインスタンス化するために必要なコンストラクターを定義します。 既定の .xib テンプレートには "非デザイナー" クラスのパーツがありますが、.xib で定義する他のカスタム クラスについては、非デザイナーのパーツを手動で追加する必要があります。

部分クラスを使用したこの区別は、柔軟性のために必要です。 たとえば、複数の CodeBehind クラスは、IB によってサブクラス化されるクラスをサブクラス化する共通のマネージド抽象クラスをサブクラス化することがあります。

CodeBehind クラスを、{0}.xib.designer.cs デザイナー ファイルの横にある {0}.xib.cs ファイルに配置するのが従来の方法です。

生成されたアクションとアウトレット

部分デザイナー クラスでは、Visual Studio for Mac によって、IB で定義されている接続されているアウトレットに対応するプロパティと、接続されているアクションに対応する部分メソッドが生成されます。

アウトレットのプロパティ

デザイナー クラスには、カスタム クラスで定義されているすべてのアウトレットに対応するプロパティが含まれています。 これらのプロパティによって、遅延バインディングが可能になります。 これは、Xamarin.iOS から Objective C へのブリッジの実装の詳細です。 これらのフィールドは、CodeBehind クラスからのみ使用することを目的としたプライベート フィールドと同等と考えてください。 非デザイナーのクラス パーツのフィールドにパブリック アクセサーを追加して、フィールドをパブリックにします。

アウトレット プロパティが id 型 (NSObject と同等) をもつよう定義されている場合、デザイナー コード ジェネレーターは現在、便宜上、そのアウトレットに接続されているオブジェクトに基づいて、可能な限り厳密に型を決定します。 ただし、この動作は将来のバージョンではサポートされない可能性があります。 カスタム クラスを定義するときは、アウトレットを明示的に厳密に型指定することをお勧めします。

アクション プロパティ

デザイナー クラスには、カスタム クラスで定義されているすべてのアクションに対応する部分メソッドが含まれています。 これらのメソッドには実装がありません。 部分メソッドの目的は次の 2 つです。

  1. 非デザイナー クラス パーツのクラス本体で「partial」と入力すると、Visual Studio for Mac では、実装されていないすべての部分メソッドのシグネチャをオートコンプリートするよう提案されます。
  2. 部分メソッド シグネチャには、それらを Objective-C ワールドに公開する属性が適用されているため、対応するアクションとして処理できます。

部分メソッドを無視し、別のメソッドに属性を適用してアクションを実装することもできます。 または、基底クラスに落とします。

アクションが id 型 (NSObject と同等) をもつよう定義されている場合、デザイナー コード ジェネレーターは現在、そのアウトレットに接続されているアクションに基づいて、可能な限り厳密に型を決定します。 ただし、この動作は将来のバージョンではサポートされない可能性があります。 カスタム クラスを定義するときは、アクションを明示的に厳密に型指定することをお勧めします。

CodeDOM では部分メソッドがサポートされていないため、これらの部分メソッドは C# に対してのみ作成されます。 他の言語に対しては生成されません。

クロス XIB クラスの使用法

タブ コントローラーなど、複数の .xib ファイルから同じクラスを参照する必要がある場合があります。 別の .xib ファイルからクラス定義を明示的に参照することも、2 番目の .xib で同じクラス名を再び定義することもできます。

後者の場合は、Visual Studio for Mac が .xib ファイルを個別に処理するため、問題が発生する可能性があります。 Visual Studio for Mac では、重複する定義を検出してマージすることはできません。 同じ部分クラスが複数のデザイナー ファイルで定義されている場合、Register 属性を複数回適用する競合が発生する可能性があります。 Visual Studio for Mac の最近のバージョンでは競合の解決の試みが行われていますが、想定どおりに動作するとは限りません。 今後、この動作はサポートされなくなる可能性があります。代わりに、Visual Studio for Mac では、プロジェクト内のすべての .xib ファイルとマネージド コードで定義されているすべての型が、すべての .xib ファイルから直接見られるようにしようとしています。

型の解決

IB で使用される型は、Register 属性を使用して CLR 型にマップされる Objective-C 型名です。 コードを生成すると、Visual Studio for Mac によって CLR 型が解決され、型名が Objective-C 型に完全に修飾されます。 これらの Objective-C 型は、Xamarin.iOS コアによってラップされます。

コード ジェネレーターでは現在、ユーザー コードまたはライブラリ内の Objective-C 型名から CLR 型を解決できません。 このような場合は、型名を文字どおりに出力します。 CLR 型を適切に解決するには、Objective-C 型と同じ名前である必要があります。 CLR 型は、それを使用しているコードと同じ名前空間に存在する必要があります。 今後のバージョンのコード ジェネレーターでは、プロジェクト内のすべての Objective-C 型が考慮されます。