Xamarin.iOS でのプログラム上のレイアウト制約

このガイドでは、iOS 自動レイアウトの制約を iOS Designer で作成するのではなく、C# コードでこれに対処する方法について説明します。

自動レイアウト ("アダプティブ レイアウト" とも呼ばれます) は、応答性の高い設計アプローチです。 各要素の位置が画面上のポイントにハードコーディングされている移行レイアウト システムとは異なり、自動レイアウトは、デザイン サーフェイス上での要素の他の要素に対する相対的な位置であるリレーションシップに関するものです。 自動レイアウトの中心にあるのは、画面上の他の要素のコンテキストにおける要素または要素のセットの配置を定義する制約またはルールの概念です。 要素は画面上の特定の位置に関連付けられていないため、さまざまな画面サイズとデバイスの向きに適したアダプティブ レイアウトを作成するのに制約が役立ちます。

通常、iOS で自動レイアウトを使用する場合は、Xcode の Interface Builder を使用して UI 項目にレイアウト制約をグラフィカルに配置します。 ただし、C# コードで制約を作成して適用する必要がある場合があります。 たとえば、UIView に追加された、動的に作成された UI 要素を使用する場合などです。

このガイドでは、Xcode の Interface Builder で制約をグラフィカルに作成するのではなく、C# コードを使用して制約を作成して操作する方法について説明します。

プログラムによる制約の作成

前述のように、通常は iOS Designer で自動レイアウトの制約を処理します。 プログラムで制約を作成する必要がある場合は、次の 3 つのオプションから選択できます。

  • レイアウト アンカー - この API は、制約されている UI 項目のアンカー プロパティ (TopAnchorBottomAnchor または HeightAnchor など) へのアクセスを提供します。
  • レイアウト制約 - NSLayoutConstraint クラスを使用して直接制約を作成できます。
  • 視覚成形言語 - 制約を定義するために、ASCII アートのようなメソッドを提供します。

以降のセクションでは、各オプションについて詳しく説明します。

レイアウト アンカー

NSLayoutAnchor クラスを使用すると、制約対象の UI 項目のアンカー プロパティに基づいて制約を作成するために Fluent インターフェイスを使用できます。 たとえば、ビュー コントローラーの上下のレイアウト ガイドには、TopAnchorBottomAnchorHeightAnchor のアンカー プロパティが表示され、ビューではエッジ、中心、サイズ、ベースラインのプロパティが表示されます。

重要

iOS ビューには、アンカー プロパティの標準セットに加えて、LayoutMarginsGuidesReadableContentGuide のプロパティも含まれます。 これらのプロパティは、ビューの余白と読み取り可能なコンテンツ ガイドをそれぞれ操作するための UILayoutGuide オブジェクトを公開します。

レイアウト アンカーには、読みやすくコンパクトな形式で制約を作成するためのいくつかのメソッドが用意されています。

  • ConstraintEqualTo - first attribute = second attribute + [constant] で、必要に応じて指定された constant のオフセット値を持つリレーションシップを定義します。
  • ConstraintGreaterThanOrEqualTo - first attribute >= second attribute + [constant] で、必要に応じて指定された constant のオフセット値を持つリレーションシップを定義します。
  • ConstraintLessThanOrEqualTo - first attribute <= second attribute + [constant] で、必要に応じて指定された constant のオフセット値を持つリレーションシップを定義します。

次に例を示します。

// Get the parent view's layout
var margins = View.LayoutMarginsGuide;

// Pin the leading edge of the view to the margin
OrangeView.LeadingAnchor.ConstraintEqualTo (margins.LeadingAnchor).Active = true;

// Pin the trailing edge of the view to the margin
OrangeView.TrailingAnchor.ConstraintEqualTo (margins.TrailingAnchor).Active = true;

// Give the view a 1:2 aspect ratio
OrangeView.HeightAnchor.ConstraintEqualTo (OrangeView.WidthAnchor, 2.0f);

一般的なレイアウト制約は、線形式として簡単に表現できます。 次に例を示します。

A Layout Constraint expressed as a linear expression

これは、レイアウト アンカーを使用して次の C# コード行に変換されます。

PurpleView.LeadingAnchor.ConstraintEqualTo (OrangeView.TrailingAnchor, 10).Active = true; 

ここで、C# コードの部分は、次のように式の指定された部分に対応します。

方程式 コード
項目 1 PurpleView
属性 1 LeadingAnchor
関係 ConstraintEqualTo
乗数 既定値は 1.0 なので、指定されていません
項目 2 OrangeView
属性 2 TrailingAnchor
定数 10.0

特定のレイアウト制約の式を解決するために必要なパラメーターのみを指定するだけでなく、レイアウト アンカーの各メソッドでは、渡されるパラメーターのタイプ セーフが適用されます。 したがって、LeadingAnchorTrailingAnchor などの水平制約アンカーは、他の水平アンカー タイプでのみ使用でき、乗数はサイズ制約にのみ提供されます。

レイアウトの制約

自動レイアウト制約は、C# コードで NSLayoutConstraint を直接構築することで手動で追加できます。 レイアウト アンカーを使用する場合とは異なり、定義されている制約に影響を与えない場合でも、すべてのパラメーターに値を指定する必要があります。 その結果、読みにくい定型コードが大量に生成されます。 次に例を示します。

//// Pin the leading edge of the view to the margin
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Leading, NSLayoutRelation.Equal, View, NSLayoutAttribute.LeadingMargin, 1.0f, 0.0f).Active = true;

//// Pin the trailing edge of the view to the margin
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Trailing, NSLayoutRelation.Equal, View, NSLayoutAttribute.TrailingMargin, 1.0f, 0.0f).Active = true;

//// Give the view a 1:2 aspect ratio
NSLayoutConstraint.Create (OrangeView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, OrangeView, NSLayoutAttribute.Width, 2.0f, 0.0f).Active = true;

ここで、NSLayoutAttribute 列挙型はビューの余白の値を定義して LeftRightTopBottom などの LayoutMarginsGuide プロパティに対応し、NSLayoutRelation 列挙型は、指定された属性間に作成されるリレーションシップを EqualLessThanOrEqual、または GreaterThanOrEqual として定義します。

Layout Anchor API とは異なり、NSLayoutConstraint の作成方法では特定の制約の重要な側面が強調表示されず、制約に対してコンパイル時チェックは実行されません。 その結果、ランタイムに例外をスローする無効な制約が容易に構築されます。

視覚形成言語

視覚形成言語を使用すると、作成される制約を視覚的に表現する ASCII アートのような文字列を使用して制約を定義できます。 これには、次の長所と短所があります。

  • 視覚形成言語では、有効な制約の作成のみが適用されます。
  • 自動レイアウトでは、視覚形成言語を使用してコンソールに制約が出力されるため、デバッグ メッセージは制約の作成に使用されるコードに似ています。
  • 視覚形成言語を使用すると、非常にコンパクトな式で複数の制約を同時に作成できます。
  • 視覚形成言語文字列のコンパイル側の検証がないため、問題はランタイムにのみ検出できます。
  • 視覚形成言語では完全性よりも視覚化が重視されるため、一部の制約の種類 (比率など) を作成することはできません。

視覚形成言語を使用して制約を作成する場合は、次の手順を実行します。

  1. ビュー オブジェクトとレイアウト ガイド、および書式を定義するときに使用される文字列キーを含む NSDictionary を作成します。
  2. 必要に応じて、制約の定数値として使用されるキーと値 (NSNumber) のセットを定義する NSDictionary を作成します。
  3. 1 列または 1 行の項目をレイアウトする書式指定文字列を作成します。
  4. NSLayoutConstraint クラスの FromVisualFormat メソッドを呼び出して制約を生成します。
  5. NSLayoutConstraint クラスの ActivateConstraints メソッドを呼び出して、制約をアクティブにして適用します。

たとえば、視覚形成言語で先頭と末尾の両方の制約を作成するには、次を使用できます。

// Get views being constrained
var views = new NSMutableDictionary (); 
views.Add (new NSString ("orangeView"), OrangeView);

// Define format and assemble constraints
var format = "|-[orangeView]-|";
var constraints = NSLayoutConstraint.FromVisualFormat (format, NSLayoutFormatOptions.AlignAllTop, null, views);

// Apply constraints
NSLayoutConstraint.ActivateConstraints (constraints);

視覚形成言語では、既定の間隔を使用するときに、常に親ビューの余白に 0 ポイントの制約がアタッチされるため、このコードは上記の例と同じ結果を生成します。

1 行に複数の子ビューがある場合など、より複雑な UI デザインの場合、視覚形成言語は水平方向の間隔と垂直方向の配置の両方を指定します。 AlignAllTop を指定する上の例のように、NSLayoutFormatOptions は行または列内のすべてのビューを上に揃えます。

一般的な制約と視覚形成文字列の文法を指定する例については、Apple の視覚形成言語の付録を参照してください。

まとめ

このガイドでは、自動レイアウト制約を iOS Designer でグラフィカルに作成するのではなく、C# で作成して操作する方法について説明しました。 最初に、レイアウト アンカー (NSLayoutAnchor) を使用して自動レイアウトを処理することを確認しました。 次に、レイアウト制約 (NSLayoutConstraint) を操作する方法を示しました。 最後に、自動レイアウトへの視覚形成言語の使用方法を示しました。