2

したがって、2 つの長方形のビューと、それらを接続するビューがあります。図を参照してください http://cl.ly/image/340Q1l381b3L

緑のビューの垂直境界内に留まるように赤のビューを制限することは可能ですか? 重なり合う領域の中央に配置したいと思います。

これは必ずしも中央に配置されるわけではありませんが、次の 2 つの制約を緑のビューと赤のビューの両方に適用して、少なくとも赤のビューを緑のビューにバインドしておくことができると考えました。

[NSLayoutConstraint
 constraintWithItem:redView
 attribute:NSLayoutAttributeCenterY
 relatedBy:NSLayoutRelationGreaterThanOrEqual
 toItem:greenView
 attribute:NSLayoutAttributeBottom
 multiplier:1.0f
 constant:0.0f];

[NSLayoutConstraint
 constraintWithItem:redView
 attribute:NSLayoutAttributeCenterY
 relatedBy:NSLayoutRelationLessThanOrEqual
 toItem:greenView
 attribute:NSLayoutAttributeTop
 multiplier:1.0f
 constant:0.0f];

しかし、これは

2013-06-26 22:13:27.493 MiniMeasure[25896:303] 制約を同時に満たすことができません: ( "NSLayoutConstraint:0x1002cebf0 RedView:0x1002cdf90.centerY >= GreenView:0x1018a34c0.bottom", "NSLayoutConstraint:0x1002cf720 RedcenterY:0x1002cdf99 <= GreenView:0x1018a34c0.top", "NSLayoutConstraint:0x1002bb1e0 V:[GreenView:0x1018a34c0(157)]" ) 制約 NSLayoutConstraint:0x1002bb1e0 V:[GreenView:0x1018a34c0(157)] を破って回復を試みます

明らかに、緑色のビューの 1 つに高さの制約があり、それを破ろうとするのは好ましくありません。しかし、サイズを維持するために緑色のビューも必要です。緑のビューには、幅と高さの制約があります。

考えや提案はありますか?

ありがとう!

4

2 に答える 2

12

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 でプロジェクトを作成しました。

于 2013-06-28T06:36:43.873 に答える
0

コメントに基づいて、1 つの解決策は、赤い四角形に自動レイアウトを使用せず、スーパー ビューまたはスーパー ビューと同じフレーム四角形であるサブビューで drawRect を使用することです。

最初に KVO を使用して、境界を構成する緑色の四角形の変更を通知します (実装に応じて)

かわった?

2 つの緑色の四角形で NSUnionRect を使用すると、いくつかの if ステートメントで赤い四角形を表示するかどうかを判断し、四角形を描画または配置する場所のロジックを提供できます。

合体四角形が一方向の緑の四角形の合計よりも大きく、他の方向の和よりも小さい場合は、赤の四角形を描画します。

エッジを比較することで、どこに描画することができます。

疑似コード unionRect.size.x > (greenRect1.size.x + greenRect2.size.x) AND unionRect.size.y < (greenRect1.size.y + greenRect2.size.y) の場合、それらの間に赤い四角形を描画します。水平に。次に、赤い四角形の四角形を計算します。

この同じアプローチを使用して、緑の四角形に対して赤の四角形に使用する制約を見つけ出すことができますが、実際には複雑さが増します。

作業量と複雑さ、および意味の難読化の可能性のために、すべてが自動レイアウトに属しているとは確信していません。

于 2013-06-28T00:22:08.837 に答える