2

所以我有两个矩形视图和一个将它们连接在一起的视图。见图表http://cl.ly/image/340Q1l381b3L

是否可以限制红色视图使其保持在绿色视图垂直范围内?我想把它放在他们重叠的区域的中心。

尽管这不一定使它居中,但我认为我只能将以下两个约束应用于绿色视图和红色视图,以至少将红色视图绑定到绿色视图。

[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 RedView:0x1002c <= GreenView:0x1018a34c0.top", "NSLayoutConstraint:0x1002bb1e0 V:[GreenView:0x1018a34c0(157)]" ) 将尝试通过打破约束来恢复 NSLayoutConstraint:0x1002bb1e0 V:[GreenView:0x1018a34c0(157)]

很明显,它不喜欢其中一个绿色视图有高度限制并试图打破它。但我也需要绿色视图来保持它们的大小。两个绿色视图都有宽度和高度限制。

有什么想法/建议吗?

谢谢!

4

2 回答 2

12

你可以通过自动布局得到你想要的。示范:

演示

但是,如果绿色视图没有任何垂直重叠,则结果可能不是您想要的:

在此处输入图像描述

那么,我是怎么做到的呢?

首先,有五种观点。有窗口的内容视图、两个可拖动的绿色视图、文本字段(“中心”)和棕褐色间隔视图。可拖动视图、文本字段和分隔符都是窗口内容视图的直接子视图。特别是,文本字段不是间隔视图的子视图。

其次,我需要设置一些约束的优先级,所以我定义了一个辅助函数:

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 约束,因此我直接设置了该约束,而不是使用可视格式。

我非常相似地创建了正确的可拖动视图,除了它被锚定到根视图的后缘。

- (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]];

接下来,我们将 spacer 的顶部和底部边缘约束为等于根视图的顶部和底部边缘,但优先级为 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)];

所以垫片的上边缘有三个约束:

  • spacerView.top == rootView.top 优先级为 1
  • spacerView.top >= leftView.top 优先级为 2
  • spacerView.top >= rightView.top 优先级为 2

这些约束结合起来,使垫片的顶部边缘尽可能高,同时不高于两个可拖动视图的顶部边缘。因此,间隔顶部边缘将等于可拖动视图的顶部边缘中的较低者。

我们需要在这里使用低优先级,因为自动布局将拒绝使垫片的高度为负。如果我们使用优先级 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,固定到可拖动视图的近边缘之一。

最后,我将文本字段设置为在分隔符上水平和垂直居中:

- (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]];
}

由于五个视图(包括根视图)之间的所有这些约束,自动布局使中间视图以您请求的方式居中。

我不确定从 Xcode 4.6.3 开始是否可以在 nib 中设置这样的约束。如果可能的话,我敢肯定这是非常痛苦的。

您可以在这个 github 存储库中找到我的完整演示项目源代码。我在 Xcode 5-DP2 中创建了项目。

于 2013-06-28T06:36:43.873 回答
0

根据评论,一种解决方案是不对红色矩形使用自动布局,而是在超级视图或与超级视图相同的框架矩形的子视图中简单地使用 drawRect。

首先使用 KVO 来通知我们对绿色矩形框的更改(适合您的实现)

改变了吗?

如果您在两个绿色矩形上使用 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 回答