autolayout で必要なものを取得できます。デモンストレーション:
ただし、緑色のビューに垂直方向のオーバーラップがない場合、結果は希望どおりにならない可能性があります。
それで、どうやってそれをしたのですか?
まず、5 つのビューがあります。ウィンドウのコンテンツ ビュー、ドラッグ可能な 2 つの緑色のビュー、テキスト フィールド (「中央」)、およびタン スペーサー ビューがあります。ドラッグ可能なビュー、テキスト フィールド、およびスペーサーはすべて、ウィンドウのコンテンツ ビューの直接のサブビューです。特に、テキスト フィールドはスペーサー ビューのサブビューではありません。
次に、いくつかの制約の優先度を設定する必要があるため、ヘルパー関数を定義します。
static NSLayoutConstraint *constraintWithPriority(NSLayoutConstraint *constraint,
NSLayoutPriority priority)
{
constraint.priority = priority;
return constraint;
}
次に、左側にドラッグ可能なビューを作成します。X 位置と幅、高さ、Y 位置の制約を設定しました。
- (void)createLeftView {
leftView = [[DraggableView alloc] init];
leftView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:leftView];
NSDictionary *views = NSDictionaryOfVariableBindings(leftView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-(20)-[leftView(80)]"
options:0 metrics:nil views:views]];
[leftView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[leftView(120)]"
options:0 metrics:nil views:views]];
leftView.yConstraint = [NSLayoutConstraint
constraintWithItem:leftView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:rootView attribute:NSLayoutAttributeTop
multiplier:1 constant:20];
[rootView addConstraint:leftView.yConstraint];
}
DraggableView
私のクラスは、その定数を調整することでマウスのドラッグを処理することに注意してくださいyConstraint
。Top コンストレイントにアクセスする必要があるため、ビジュアル フォーマットを使用する代わりに、Top コンストレイントを直接設定します。
ルート ビューの後縁に固定されていることを除いて、右のドラッグ可能なビューを非常によく似た方法で作成します。
- (void)createRightView {
rightView = [[DraggableView alloc] init];
rightView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:rightView];
NSDictionary *views = NSDictionaryOfVariableBindings(rightView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[rightView(80)]-(20)-|"
options:0 metrics:nil views:views]];
[rightView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:[rightView(120)]"
options:0 metrics:nil views:views]];
rightView.yConstraint = [NSLayoutConstraint
constraintWithItem:rightView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:rootView attribute:NSLayoutAttributeTop
multiplier:1 constant:20];
[rootView addConstraint:rightView.yConstraint];
}
ここで、トリッキーな部分が来ます。スペーサーと呼ぶ追加のビューを作成します。このデモがどのように機能するかを理解しやすくするために、ビューを表示したままにしています。通常、スペーサーは非表示にします。非表示のビューは引き続きレイアウトに参加します。
- (void)createSpacerView {
spacerView = [[SpacerView alloc] init];
spacerView.translatesAutoresizingMaskIntoConstraints = NO;
[rootView addSubview:spacerView];
スペーサーの水平方向の制約は単純です。スペーサーの前縁は左のドラッグ可能なビューの後縁に固定され、スペーサーの後縁は右のドラッグ可能なビューの前縁に固定されます。したがって、スペーサーは常に、ドラッグ可能な (緑色の) ビュー間の水平方向のギャップに正確にまたがります。
NSDictionary *views = NSDictionaryOfVariableBindings(leftView, rightView, spacerView);
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[leftView][spacerView][rightView]"
options:0 metrics:nil views:views]];
次に、スペーサーの上端と下端をルート ビューの上端と下端と等しくなるように制約しますが、優先度は 1 (非常に低い優先度) です。
[rootView addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-(0@1)-[spacerView]-(0@1)-|"
options:0 metrics:nil views:views]];
次に、スペーサーの上端が、ドラッグ可能なビューの両方の上端の上端以上になるように、優先度 2 (ほぼ同じくらい非常に低い) でさらに制約します。
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:leftView attribute:NSLayoutAttributeTop
multiplier:1 constant:0], 2)];
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:rightView attribute:NSLayoutAttributeTop multiplier:1 constant:0], 2)];
したがって、スペーサーの上端には 3 つの制約があります。
- spaceView.top == 優先度 1 の rootView.top
- spaceView.top >= leftView.top 優先度 2
- spaceView.top >= 優先度 2 の rightView.top
これらの制約を組み合わせて、ドラッグ可能なビューの両方の上端より上にならないようにしながら、スペーサーの上端をできるだけ高く配置します。したがって、スペーサーの上端は、ドラッグ可能なビューの上端の下部と等しくなります。
自動レイアウトはスペーサーの高さを負にすることを拒否するため、ここでは低い優先度を使用する必要があります。優先度 1000 (デフォルト) を使用した場合、自動レイアウトはドラッグ可能なビューのサイズ変更を開始し、常に垂直方向のオーバーラップを強制します。
スペーサーの下端に同様の制約を設定します。
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:leftView attribute:NSLayoutAttributeBottom
multiplier:1 constant:0], 2)];
[rootView addConstraint:constraintWithPriority([NSLayoutConstraint
constraintWithItem:spacerView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:rightView attribute:NSLayoutAttributeBottom
multiplier:1 constant:0], 2)];
}
したがって、オーバーラップがある場合、スペーサーは常にドラッグ可能なビューの垂直方向のオーバーラップにまたがります。オーバーラップがない場合、自動レイアウトはいくつかの制約を破り、スペーサーに負の高さを与えないようにします。スペーサーは最終的に高さ 0 になり、ドラッグ可能なビューの近くの端の 1 つに固定されます。
最後に、テキスト フィールドがスペーサーの水平方向および垂直方向の中央に配置されるように設定します。
- (void)createMiddleView {
middleView = [[NSTextField alloc] init];
middleView.translatesAutoresizingMaskIntoConstraints = NO;
middleView.stringValue = @"Center";
[middleView setEditable:NO];
[middleView setSelectable:NO];
[rootView addSubview:middleView];
[rootView addConstraint:[NSLayoutConstraint
constraintWithItem:middleView attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:spacerView attribute:NSLayoutAttributeCenterX
multiplier:1 constant:0]];
[rootView addConstraint:[NSLayoutConstraint
constraintWithItem:middleView attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:spacerView attribute:NSLayoutAttributeCenterY
multiplier:1 constant:0]];
}
5 つのビュー (ルート ビューを含む) 間のこれらすべての制約により、自動レイアウトは中央のビューを要求どおりに中央に配置します。
Xcode 4.6.3 の時点で nib にこのような制約を設定できるかどうかはわかりません。もしそれが可能であるならば、それは非常に苦痛であると確信しています。
私の完全なデモ プロジェクトのソース コードは、この github リポジトリにあります。Xcode 5-DP2 でプロジェクトを作成しました。