web-dev-qa-db-ja.com

自動レイアウトのスプリング:Xcode 5で、制約付きでビューを均等に分配します

Interface Builderでビューを整列、サイズ変更、配布する古いStruts and Springsの方法を理解しています。ただし、Xcode 5で自動レイアウトを使用してビューを均等に分散する方法を理解できないようです。Xcode4を使用してそれを行う方法はありましたが、そのオプションはなくなりました。

7つのボタンが縦に並んでいます。 3.5インチレイアウトでは、見栄えがよくなります。4インチレイアウトで画面をプレビューすると、すべてのボタンがぎっしり詰まっており、最後のボタンの下に大量のスペースがあります。

私はそれらを同じ高さに保ちたいが、それらが画面全体に広がることができるようにそれらの間のスペースが曲がることができるようにしたい。

enter image description here

ボタンの高さを調整してスペースを埋めることができましたが、それは望ましい動作ではありません。 Auto Layoutを使用して古いSpringsの動作を置き換える方法を学びたいのですが、Interface Builderでそれを行う方法が見つからないようです。

上ボタンは、下ボタンと下エッジも同様に、上エッジからの固定スペースでも、上エッジからの比例スペースでも構いません。それらは私にとってそれほど重要ではありません、私はどちらでも良いです。

しかし、ビュー内の各アイテム間に余分なスペースを均等に分散する方法を実際に把握する必要があります。

42
Kenny Wyland

[〜#〜] edit [〜#〜]iOS 9では、UIStackViewが配布を自動的に実行するため、この手法は不要になることに注意してください。 別の答え を追加して、その仕組みを説明します。

自動レイアウトを使用して均等配布を実行する方法

Interface Builderのみでこれを行う最も簡単な方法は(コードで制約を構築するのではなく)、「スペーサー」ビューを使用することです。

  1. 上下のボタンを絶対に配置します。

  2. すべてのボタンの間にスペーサービューを配置します。制約を使用して、水平方向に配置し(水平方向に中央に配置するのが最も簡単です)、幅を設定します。

  3. 定数を0にして、各ボタンとその上下のスペーサービューの間に制約を設定します。

  4. すべてのスペーサービューを選択し、それらの高さを等しく設定します。

最初のスクリーンショットは、これをIBで設定することを示しています。

enter image description here

制約を設計している間、どのように見えるかを見てほしいので、「見当違いのビュー」については意図的に修正していません。 4インチ画面と3.5インチ画面の両方での結果は次のとおりです。

enter image description here

このテクニックがどのように機能するかを示すために、スペーサービューを黒のままにしましたが、もちろん実際には、それらを透明にして非表示にします!そのため、ユーザーにはボタンが表示され、画面のどちらの高さに均等に配置されます。

この手法を使用する理由は、平等の概念が求めている値の分布を実行しますが、制約はビューのアスペクト間でのみ平等を適用できるためです。したがって、他の物と同等にすることができるようにするために、追加のビュー(スペーサービュー)が必要です(ここでは、スペーサービューの高さ)。

その他のアプローチ

明らかに、より柔軟なアプローチは、コードで制約を割り当てることです。これは気が遠くなるかもしれませんが、 この種のこと のような、あなたを助けるサードパーティのコードがたくさんあります。

たとえば、高さが4つのボタンの最大垂直分布を指示する境界として機能する(おそらく不可視の)スーパービューがある場合、constant0を使用して、トップをそのスーパービューの垂直中央に固定できます。しかし、0.0000010.6666671.333332.0multiplier(4つのボタンがある場合);画面の高さなどに応じてスーパービューのサイズが変更されても、ボタンは垂直に配置されたままになります。 [Xcode 5.1では、Interface Builderで設定できますが、Xcodeの以前のバージョンでは設定できません。]

84
matt

IOS 9/Xcode 7では、この問題はIBで簡単に解決されます。ボタン(または垂直に配置するもの)を選択し、[エディター]> [埋め込み]> [スタックビュー]を選択します。次に、単にスタックビューを構成します。

  • スタックビュー自体の位置とサイズを決める制約を提供します。たとえば、スタックビューの4つのエッジをスーパービューの4つのエッジに固定します。

  • スタックビューの属性を設定します。この場合、垂直軸、塗りつぶしの配置、等間隔の分布が必要です。

それで全部です!ただし、コードで同じことを手動で行うことはまだ可能であるため、これがどのように機能するのか興味があります。スタックビューは、スペーサーviewsを挿入するのではなく、スペーサーguidesを挿入することによって配信を実行します。ガイド(UILayoutGuide)は、レイアウトの制約のためにビューのように動作する軽量のオブジェクトですが、ビューではないため、非表示にする必要がなく、ビューのオーバーヘッドを一切持ちません。

説明のために、スタックビューの動作をコードで説明します。垂直に分散する4つのビューがあるとします。分布以外のすべてに制約を割り当てます。

  • それらはすべて絶対的な高さの制約があります

  • 左がスーパービューの左に固定され、右がスーパービューの右に固定されています

  • トップビューの上部はスーパービューの上部に固定され、ボトムビューの下部はスーパービューの下部に固定されます

ここで、4つのビューへの参照がviews、配列としてあると仮定します。次に:

let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()]
for guide in guides {
    self.view.addLayoutGuide(guide)
}
NSLayoutConstraint.activateConstraints([
    // guide heights are equal
    guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    // guide widths are arbitrary, let's say 10
    guides[0].widthAnchor.constraintEqualToConstant(10),
    guides[1].widthAnchor.constraintEqualToConstant(10),
    guides[2].widthAnchor.constraintEqualToConstant(10),
    // guide left is arbitrary, let's say superview margin
    guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    // bottom of each view is top of following guide
    views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor),
    views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor),
    views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor),
    // top of each view is bottom of preceding guide
    views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor),
    views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor),
    views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor)
])

(明らかに、ループを使用してコードをより短く、短くすることもできますが、パターンとテクニックを確認できるように、明確にするために意図的にループを展開しました。)

10
matt