制約の構造
ビュー階層のレイアウトは、連立一次方程式として定義します。制約はそれぞれ、ひとつの方程式を表します。目標は、解がただひとつ定まる連立方程式を宣言することです。
方程式の例を以下に示します。
この制約は、赤いビュー(RedView)の前辺(Leading)が、青いビュー(BlueView)の後辺(Trailing)よりも、8.0ポイント後でなければならないことを言明しています。式にはいくつかの構成要素があります。
項目1。式に現れる第1の項目。この場合は赤いビュー。ビューまたはレイアウトガイドでなければなりません。
属性1。項目1のうち、制約を受ける属性。この場合は赤いビューの前辺。
関係。左辺と右辺の関係。「等値」、「以上」、「以下」のいずれか。この場合、左辺と右辺は等値。
乗数。属性2の値に乗じる浮動小数点数。この場合の乗数は
1.0
。項目2。式に現れる第2の項目。この場合は青いビュー。項目1と違い、空であっても可。
属性2。項目2のうち、制約を受ける属性。この場合は青いビューの後辺。項目2が空であれば「
Not an Attribute
」とする。定数。浮動小数点数の定数。この場合は
8.0
。これを属性2の値に加算。
制約の多くは、画面上の2つの項目の関係を定義するものです。項目になりうるのはビューまたはレイアウトガイドです。同じ項目の2つの属性について、制約を定義することも可能です。たとえば、ある項目の高さと幅に、アスペクト比を設定することが考えられます。高さや幅として定数を与えることも可能です。この場合、項目2は空、属性2は「Not An Attribute
」、乗数は0.0
としてください。
Auto Layoutの属性
Auto Layoutでは、属性は制約が課される特性を表します。一般に、4つの辺(前辺、後辺、上辺、下辺)や、高さ、幅、縦中央、横中央などが属性となります。テキスト項目にはベースライン属性もいくつかあります。
属性の完全な一覧については、列挙型NSLayoutAttribute
の定義を参照してください。
方程式の例
さまざまなパラメータや属性を方程式に組み入れて、各種の制約を定義できます。たとえば、ビューとビューとの間隔、ビューの辺の揃え、2つのビューの寸法比、ビューのアスペクト比などがあります。もっとも、どんな属性でも組み合わせてよいわけではありません。
属性には、大きく分けて2つの種類があります。寸法の属性(たとえば、HeightやWidth)と位置の属性(たとえば、Leading、Left、Top)です。寸法の属性は、項目の位置を示さずに大きさを指定するために使います。位置の属性は、ほかの項目を基準として、項目の相対的な位置を指定するために使います。ただし、項目の寸法情報をこれらの属性で指定することはできません。
これらの違いがあるほかに、以下の規則が適用されます。
寸法属性の値を、位置属性の値の範囲に収めることはできません。
位置属性に定数値を代入することはできません。
非一致乗数(1.0以外の値)を位置属性とともに使うことはできません。
位置属性の場合、縦方向属性の値を横方向属性の値の範囲に収めることはできません。
位置属性の場合、Leading属性またはTrailing属性の値をLeft属性またはRight属性の値の範囲に収めることはできません。
たとえば、項目のTopを定数値20.0に設定しても、コンテキストをさらに指定しない限り意味はありません。項目の位置属性は、他の項目との関係で定義しなければなりません。たとえば、上位ビューの上辺よりも20.0ポイント下、という形です。ただし、項目のHeightを20.0にするのは問題なく有効です。詳しくは、「値の解釈」を参照してください。
リスト3-1に、よく現れる制約を表す方程式の例を示します。
// Setting a constant height
View.height = 0.0 * NotAnAttribute + 40.0
// Setting a fixed distance between two buttons
Button_2.leading = 1.0 * Button_1.trailing + 8.0
// Aligning the leading edge of two buttons
Button_1.leading = 1.0 * Button_2.leading + 0.0
// Give two buttons the same width
Button_1.width = 1.0 * Button_2.width + 0.0
// Center a view in its superview
View.centerX = 1.0 * Superview.centerX + 0.0
View.centerY = 1.0 * Superview.centerY + 0.0
// Give a view a constant aspect ratio
View.height = 2.0 * View.width + 0.0
等号が代入ではなく等値関係を表すこと
重要なのは、「よく現れる制約を表す方程式の例」の式に現れる等号が、代入ではなく等値関係を表すことです。
Auto Layoutが方程式を解く際、単に右辺値を左辺に代入しているのではありません。属性1と属性2の値が関係を満たすように計算しているのです。したがって、項目が現れる順序を入れ替えても、同じ制約を表せることが少なくありません。たとえばリスト3-2の方程式は、「注意」と同じ制約を表します。
// Setting a fixed distance between two buttons
Button_1.trailing = 1.0 * Button_2.leading - 8.0
// Aligning the leading edge of two buttons
Button_2.leading = 1.0 * Button_1.leading + 0.0
// Give two buttons the same width
Button_2.width = 1.0 * Button.width + 0.0
// Center a view in its superview
Superview.centerX = 1.0 * View.centerX + 0.0
Superview.centerY = 1.0 * View.centerY + 0.0
// Give a view a constant aspect ratio
View.width = 0.5 * View.height + 0.0
同じ問題であっても、方程式の立て方はいくつもあるのが普通です。本来は、意図を直截に記述した式を選ぶべきでしょう。しかし、具体的にどの式がそうか、となると、開発者によって見解が分かれます。ここでは、「正しいこと」よりも「一貫していること」の方が重要です。ある方針を選んだら、一貫してその方法を用いてください。そうすれば、長い目で見ると問題が生じにくいでしょう。たとえばこのガイドでは、次の方針を採用しています。
乗数はできるだけ、小数ではなく整数を選ぶ。
定数はできるだけ、負値ではなく正値を選ぶ。
できるだけビューがレイアウト順に現れるようにする(前から後、上から下の順)。
解がただひとつに定まるレイアウト
Auto Layoutを使う際の目標は、解がただひとつに定まる連立方程式を与えることです。不定な(ambiguous)制約には、解が複数あります。不能な(unsatisfiable)制約には解がありません。
一般に制約は、各ビューの寸法と位置の両方を規定するものでなければなりません。上位ビュー(たとえば、iOSのシーンのルートビュー)の寸法がすでに設定されていると仮定すると、不定でも不能でもないレイアウトを与えるためには、ビューごとに、ひとつの次元につき2つの制約が必要です(上位ビューを除く)。とはいえ、制約の選び方にはいくつもの選択肢があります。たとえば次の3つはいずれも、不定でも不能でもありません(横方向の制約のみ表示)。
第1のレイアウトは、上位ビューの前辺を基準として、ビューの前辺を制約します。また、幅は固定とします。後辺の位置は、上位ビューの寸法とその他の制約に基づいて計算できます。
第2のレイアウトは、上位ビューの前辺を基準として、ビューの前辺を制約します。さらに、上位ビューの後辺を基準として、ビューの後辺を制約します。ビューの幅は、上位ビューの寸法とその他の制約に基づいて計算できます。
第3のレイアウトは、上位ビューの前辺を基準として、ビューの前辺を制約します。さらに、ビューと上位ビューの中央を揃える旨指定します。幅と後辺の位置は、どちらも上位ビューの寸法とその他の制約に基づいて計算できます。
各レイアウトに、1つのビューと2つの横方向制約が存在しています。いずれの場合も、制約はビューの幅と横方向位置の両方を漏れなく規定するものになります。つまり、どのレイアウトの場合も、不定でも不能でもないレイアウトが横軸にそって生成されます。しかし、まったく等価というわけではありません。上位ビューの幅が変化した状況を考えてみましょう。
第1のレイアウトの場合、ビューの幅は変わりません。おそらくこれが期待する挙動ではないでしょう。一般に、ビューの寸法として定数を与えるのは避けるべきです。Auto Layoutは、環境に合わせて動的にレイアウトを決めるよう設計されています。寸法を固定すれば、この設計がまったく活かされないことになります。
ひと目で明らかとは言えせんが、第2と第3のレイアウトは挙動がまったく同じです。上位ビューの幅が変わったとき、ビューと上位ビューの間に生じる余白はどちらも一定です。しかし、まったく等価というわけではありません。一般に、第2の例は分かりやすい一方、特に多くの項目を中央揃えする場合、第3の例の方が有用です。個々のレイアウトに合わせ、最適な方法を選んでください。
次に、もう少し複雑な状況を考えてみましょう。iPhone上で、2つのビューを並べて表示したいとします。各辺ともに適切な余白を置きます。さらに、ビューの幅は常に等しくなければなりません。デバイスが回転したときも、適切に寸法を変える必要があります。
縦向きと横向きのそれぞれについて、ビューがどのようになるべきかを以下に示します。
どのような制約を課すとよいでしょうか。すぐに思いつくであろう解の例を示します。
制約を式の形に書き下すと、次のようになります。
// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Blue.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Blue.bottom + 20.0
// Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
このレイアウトには、2つのビューと、横方向、縦方向についてそれぞれ4つの制約があります。絶対に間違いのない指標とはいえないものの、方法論が適切であることはひと目で分かります。いっそう重要な点は、この制約によって両方のビューの寸法と位置が一意に規定され、不定でも不能でもないレイアウトが生成されることです。いずれかの制約を取り除けば、レイアウトは不定になります。制約を追加すれば、衝突が生じ、不能になるおそれがあります。
しかし、これが唯一の解ではありません。同じように有効な考え方を示します。
青いビューの上辺と下辺を、上位ビューにピン留めする代わりに、青いビューの上辺を赤いビューの上辺に揃えるのです。同様に、青いビューの下辺も、赤いビューの下辺に揃えます。式に書き下すと次のようになります。
// Vertical Constraints
Red.top = 1.0 * Superview.top + 20.0
Superview.bottom = 1.0 * Red.bottom + 20.0
Red.top = 1.0 * Blue.top + 0.0
Red.bottom = 1.0 * Blue.bottom + 0.0
//Horizontal Constraints
Red.leading = 1.0 * Superview.leading + 20.0
Blue.leading = 1.0 * Red.trailing + 8.0
Superview.trailing = 1.0 * Blue.trailing + 20.0
Red.width = 1.0 * Blue.width + 0.0
このレイアウトもやはり、2つのビューと、横方向、縦方向についてそれぞれ4つの制約があります。したがって同様に、不定でも不能でもないレイアウトが得られます。
制約を表す不等式
これまでに挙げた例は、制約を等式で表していましたが、話はこれで終わりません。制約は、不等式で表すことも可能です。制約の「関係」としては、「等値」、「以上」、「以下」のいずれかを指定できるのです。
たとえば、制約を使ってビューの寸法の最小値または最大値を定義できます(リスト3-3)。
// Setting the minimum width
View.width >= 0.0 * NotAnAttribute + 40.0
// Setting the maximum width
View.width <= 0.0 * NotAnAttribute + 280.0
不等式を取り入れると、「ビューごとに、ひとつの方向(次元)につき2つの制約」という規則は成り立たなくなります。実際、等式はすべて、2つの不等式に置き換えることができます。リスト3-4に、1つの等式と、同じ挙動を示す2つの不等式の組を示します。
// A single equal relationship
Blue.leading = 1.0 * Red.trailing + 8.0
// Can be replaced with two inequality relationships
Blue.leading >= 1.0 * Red.trailing + 8.0
Blue.leading <= 1.0 * Red.trailing + 8.0
逆も成り立つとは限りません。2つの不等式があるとき、1つの等式に置き換えて同じ挙動を得ることは、一般にはできないのです。たとえばリスト3-3の不等式はビューの幅として取りうる値の範囲を制限しているだけであって、幅そのものを定義しているわけではありません。この範囲内で、ビューの位置と寸法を確定するためには、ほかにも制約が必要です。
制約の優先度
デフォルトでは、制約はすべて「必須」です。Auto Layoutは制約をすべて満たす解を計算しなければなりません。できなければエラーになります。Auto Layoutは、不能な制約に関する情報をコンソールに表示した後、いずれかの制約を選択して一時的に破棄します。その上で、残りの制約を満たす解を計算します。詳しくは、「不能なレイアウト」を参照してください。
随意の(必ずしも満たさなくてよい)制約を定義することも可能です。制約にはすべて、1~1000の優先度があります。優先度1000の制約は必須です。そうでない制約は随意です。
解の計算に当たり、Auto Layoutは優先度の高い方から順に、制約をすべて満たす解を求めようとします。随意の制約を満たせなかった場合、その制約を飛ばして次の制約に進みます。
随意の制約を満たせない場合でも、レイアウトには影響が及ぶことがあります。ある制約を無視した結果、レイアウトが不定になった場合、システムはこの制約に最も「近い」解を選ぶからです。このように随意の制約には、満たされなかった場合でも、ある方向にビューを「引き寄せる」効果があります。
随意の制約と不等式を組み合わせることもよくあります。たとえばリスト3-4で、2つの不等式に異なる優先度を与えることも可能です。「以上」の関係を必須(優先度1000)とし、「以下」の関係には低い優先度(250)を与えるのです。この場合、青いビューと赤いビューの間隔が8.0ポイント未満になることはありません。しかし、他の制約との関係で、これよりも離れることはありえます。それでも、随意の制約には青いビューを赤いビューの方に「引き寄せる」効果があるため、他の制約を満たす範囲で、できるだけ8.0ポイントに近い間隔になるのです。
固有の寸法
これまでに示した例では、ビューの位置と寸法の両方に対して制約を与えました。しかし、ある種のビューには、その内容から自然に決まる寸法があります。これを固有の寸法(intrinsic content size)といいます。たとえばボタンの場合、固有の寸法は、見出し文字列の寸法に若干の余白を加えたものです。
あらゆるビューに固有の寸法があるわけではありません。固有の高さ、あるいは固有の幅しかない場合もあります。いくつかの例を表3-1に示します。
ビュー |
固有の寸法 |
---|---|
UIView、NSView |
なし |
スライダ |
幅のみ(iOS) スライダの種類に応じて、幅、高さ、または両方を定義(OS X) |
ラベル、ボタン、スイッチ、テキストフィールド |
高さと幅 |
テキストビュー、画像ビュー |
可変 |
固有の寸法は、その時点のビューの内容によって決まります。ラベルやボタンの場合、見出し文字列の量や、表示に用いるフォントによります。それ以外のビューでは、もっと複雑な決め方になります。たとえば、空の画像ビューには固有の寸法がありません。しかし画像を加えると、その寸法が固有の寸法になります。
テキストビューの場合、その内容のほか、スクロール機能の有無など、当該ビューに適用される他の制約によっても変わります。たとえば、スクロール機能が有効であれば、固有の寸法はありません。無効であれば、折り返しのないテキストの寸法に基づいて固有の寸法が計算されます。たとえば、テキストに改行がない場合は、コンテンツを1行のテキストとしてレイアウトするために必要な高さと幅が計算されます。ビューの幅を指定する制約を追加した場合、固有の寸法は、与えられた幅でテキストを表示するために必要な高さを定義するものになります。
Auto Layoutは固有の寸法を表すために、縦と横のそれぞれについて、制約の組を使います。収縮性(content hugging)とは、ビューを内側に引き寄せ、コンテンツの周囲をぴったりと包むようにする傾向のことです。一方、膨張性(compression resistance)とは、ビューを外側に押して、コンテンツが外にはみ出ないようにする傾向のことです。
この制約を定義するために、リスト3-5に示す不等式を使います。ここで、定数IntrinsicHeight
およびIntrinsicWidth
は、ビューの固有の高さ/幅を表します。
// Compression Resistance
View.height >= 0.0 * NotAnAttribute + IntrinsicHeight
View.width >= 0.0 * NotAnAttribute + IntrinsicWidth
// Content Hugging
View.height <= 0.0 * NotAnAttribute + IntrinsicHeight
View.width <= 0.0 * NotAnAttribute + IntrinsicWidth
この制約にはそれぞれ優先度があります。デフォルト値は、収縮性優先度が250、膨張性優先度が750です。したがって、縮小しようとする傾向よりも、拡大しようとする傾向の方が強いことになります。ほとんどのコントロールでは、これが望ましい挙動です。たとえば、ボタンを固有の寸法よりも大きな寸法にしても問題が生じることはありませんが、小さくした場合は表示の一部が欠ける可能性があります。なお、Interface Builderは状況により、同じ優先度の制約が現れないよう、この優先度を微調整することがあります。詳しくは、「収縮性/膨張性の優先度を設定する」を参照してください。
できるだけ、固有の寸法を使ってレイアウトを決めてください。ビューの内容が変化するのに応じ、レイアウトが動的に追随します。また、不定でも不能でもないレイアウトを決めるために必要な制約の個数を減らす、という効果もあります。その代わり、収縮性/膨張性の優先度を管理しなければなりません。固有の寸法に関するガイドラインをいくつか示します。
空き空間を埋めるためにいくつかのビューを拡大する際、収縮性優先度がすべてのビューについて同じであれば、レイアウトは不定になります。Auto Layoutはどのビューを拡大すればよいか判断できません。
典型的な例が、ラベルとテキストフィールドの組です。通常、テキストフィールドはできるだけ長くして空間を埋め、ラベルは固有の寸法のままにするのが望ましいでしょう。そのためには、テキストフィールドの収縮性優先度(横方向)を、ラベルのそれよりも低くする必要があります。
このような状況は頻繁に現れるので、Interface Builderは自動的に、ラベルの収縮性優先度を251にするようになっています。レイアウト計算プログラムを実装する場合、収縮性優先度を調整するコードを記述しなければなりません。
予期しないレイアウトが生じる典型的な例として、不可視の背景があるビュー(ボタン、ラベルなど)が、何かの拍子に固有の寸法よりも大きくなった、という状況があります。現象としては、テキストが誤った位置に現れるだけなので、真の原因に気づきにくいかも知れません。予期しない拡大を防ぐため、収縮性優先度を高くするとよいでしょう。
ベースライン制約が働くのは、固有の高さがあるビューに限ります。ビューが高さ方向に伸縮すると、ベースライン制約は正常に働かなくなります。
スイッチのように、常に固有の寸法で表示するべきビューがあります。必要ならば、寸法が変わらないよう、CHCR優先度を高くしてください。
CHCR優先度として「必須」を指定するのは避けてください。何かの拍子に衝突が起こるのに比べれば、誤った寸法で表示される方がましです。必ず固有の寸法で表示したいビューには、非常に高い優先度(999)を与えるとよいでしょう。こうすれば、ビューが伸縮することはほとんどありませんが、画面が想定外の大きさだった場合の安全弁として働きます。
固有の寸法と適合寸法
固有の寸法は、Auto Layoutに対する入力として扱われます。ビューに固有の寸法があれば、システムはこの寸法を表す制約を生成した上でレイアウトを計算します。
一方、適合寸法(fitting size)は、Auto Layoutエンジンの出力です。すなわち、ビューに対する制約をもとに計算した結果です。あるビューがその内部に、Auto Layoutで下位のビューをレイアウトした場合、システムはその結果にもとづき、当該ビューの適合寸法を計算できることがあります。
スタックビューはその典型的な例です。ほかに制約がなければ、システムはスタックビューの寸法を、その内容(下位のビュー)と属性にもとづいて計算します。その結果、スタックビューはさまざまな点で、固有の寸法があるように振る舞います。縦方向と横方向について1つずつ制約があれば、有効なレイアウトを計算できるのです。しかしこの寸法はAuto Layoutが計算した結果です。入力ではありません。スタックビューそのものには固有の寸法がないので、CHCR優先度を設定しても無意味です。
スタックビューの外部にある項目との関係で、適合寸法を調整する必要がある場合は、その関係を表す制約を明示的に定義するか、スタックの内容(下位ビュー)について、(スタックの外部にある項目との)CHCR優先度を修正してください。
値の解釈
Auto Layoutでは、値はすべてポイント単位です。しかしその正確な意味は、関与する属性や、ビューのレイアウト方向によって変わります。
Auto Layoutの属性 |
値 |
注意 |
---|---|---|
|
ビューの寸法。 |
定数値を与えるか、または他のHeight/Width属性と組み合わせ。これらの値を負の数にすることはできません。 |
|
画面の下方向に向かって増える値。 |
この属性と組み合わせてよいのは、Center Y、Top、Bottom、Baselineの各属性のみ。 |
|
後辺の方向に向かって増える値。レイアウト方向が「左から右」であれば、右に向かって増加。逆にレイアウト方向が「右から左」であれば、左に向かって増加。 |
この属性と組み合わせてよいのは、Leading、Trailing、Center Xの各属性のみ。 |
|
右に向かって増える値。 |
この属性と組み合わせてよいのは、Left、Right、Center Xの各属性のみ。 Left、Rightはできるだけ使用せず、代わりにLeadingやTrailingを使う。これにより、レイアウトをビューの書字方向に合わせて調整できるようになる。
デフォルトでは、書字方向はユーザが設定した現在の言語に基づいて決定される。ただし、これは必要に応じてオーバーライドできる。iOSでは、制約を保持しているビュー(制約の影響を受けるすべてのビューの直近の共通祖先)に対して、 |
|
式に現れる他の属性に応じて解釈。 |
Center X属性と組み合わせてよいのは、Center X、Leading、Trailing、Right、Leftの各属性のみ。 Center Y属性と組み合わせてよいのは、Center Y、Top、Bottom、Baselineの各属性のみ。 |
Copyright © 2016 Apple Inc. All rights reserved.利用規約 | プライバシーポリシー | 更新:2016/03/21