ios 使用 NSLayoutConstraint 垂直居中两个视图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17294775/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:04:52  来源:igfitidea点击:

Center two views vertically using NSLayoutConstraint

iosuikitautolayoutnslayoutconstraint

提问by Fabiano Francesconi

Imagine this following scenario. You have a UIImageView and a UIButton. The first is 300x360, the second is 210x70. The imageviewcontains a catalog image, the buttonsays "open catalog".

想象一下以下场景。你有一个 UIImageView 和一个 UIButton。第一个是 300x360,第二个是 210x70。在imageview包含目录形象,button说“公开目录”。

I would like to position elements in the main view according to these requirements:

我想根据这些要求在主视图中定位元素:

  • the two elements should be centered horizontally, namely the center.x coordinates should be all equal (view, image and button);

  • the two elements should be centered vertically in the following way: separator (flexible) - imageview - separator (fixed, let's say 30 pts) - button - separator (flexible). The topmost and bottommost separator should have the same size (this is the meaning of centered).

  • 这两个元素应该水平居中,即 center.x 坐标应该都相等(视图、图像和按钮);

  • 这两个元素应按以下方式垂直居中:分隔符(灵活) - imageview - 分隔符(固定,假设 30 pts) - 按钮 - 分隔符(灵活)。最上面和最下面的分隔符应该具有相同的大小(这就是居中的意思)。

I cannot make this to work using NSLayoutConstraint.

我无法使用 NSLayoutConstraint 使其工作。

So far, what I did, has been centering the X coordinate of the two elements using NSLayoutAttributeCenterXand NSLayoutRelationEqualto the same viewattribute.

到目前为止,我所做的是使用NSLayoutAttributeCenterX和将两个元素的 X 坐标居中NSLayoutRelationEqual到相同的view属性。

The last part, according to my idea, is to fix their vertical alignment. I tried using @"V:|-[imageview(360)]-[button(70)]-|"but it won't work (Unable to simultaneously satisfy constraints.).

根据我的想法,最后一部分是修复它们的垂直对齐。我尝试使用,@"V:|-[imageview(360)]-[button(70)]-|"但它不起作用(Unable to simultaneously satisfy constraints.)。

If I use @"V:|-[imageview(360)]-[button]-|"I get everything partially ok. Namely, the top part is perfect but the button is stretched so to fill the gap between the internal separator and the bottom of the view.

如果我使用@"V:|-[imageview(360)]-[button]-|"我得到的一切部分正常。也就是说,顶部是完美的,但按钮被拉伸以填充内部分隔符和视图底部之间的间隙。

How can I make those elements to be fixed-sized and to let Auto-layout to just figure out how to place them in the view?

我怎样才能让这些元素固定大小并让自动布局来弄清楚如何将它们放置在视图中?

回答by Fabiano Francesconi

I was able to do it by doing something like this:

我能够通过做这样的事情来做到这一点:

NSNumber *sepHeight = @60.0F;

// Center the two views horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageView
                                                      attribute:NSLayoutAttributeCenterX
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self.view
                                                      attribute:NSLayoutAttributeCenterX
                                                     multiplier:1
                                                       constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button
                                                      attribute:NSLayoutAttributeCenterX
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self.view
                                                      attribute:NSLayoutAttributeCenterX
                                                     multiplier:1
                                                       constant:0]];

// Position the two views one below the other, using the separator height defined above
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[imageview]-sepHeight-[button]"
                                                                  options:0
                                                                  metrics:NSDictionaryOfVariableBindings(sepHeight)
                                                                    views:views]];

// Force the button distance from the bottom to be the half of the size of the content
CGFloat constant = (imageview.frame.size.height + button.frame.size.height + [sepHeight floatValue]) / 2.0;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:button
                                                      attribute:NSLayoutAttributeBottom
                                                      relatedBy:NSLayoutRelationEqual
                                                         toItem:self.view
                                                      attribute:NSLayoutAttributeCenterY
                                                     multiplier:1
                                                       constant:constant]];

The tricky part is the constant value. That value is the half of the height of all the views, including their separators. This means that, if the imageview's height is 360, the button's height is 70 and the separator is 60, that constant will be (360 + 70 + 60)/2 = 245.

棘手的部分是常量值。该值是所有视图高度的一半,包括它们的分隔符。这意味着,如果图像视图的高度为 360,按钮的高度为 70,分隔符为 60,则该常量将为 (360 + 70 + 60)/2 = 245。

There should be a smarter way, indeed, but for now I think this is ok.

确实应该有更聪明的方法,但现在我认为这是可以的。

回答by TomSwift

There are two ways to approach this.

有两种方法可以解决这个问题。

  1. As @uchuugaka suggests, place your imageview and button in a container view. You don't have the same centering problem if the subviews are pinned to the edges of the container (which you would size accordingly - height would be 360 + 30 + 70 == 460. Then you can center this container in your view with a simple align-center-Y constraint.

  2. You can use spacer views. By adding two hidden views you can specify constraints to position them above/below your imageview and button, with equal heights such they act as springs. Here's code that does this, using your dimensions:

  1. 正如@uchuugaka 建议的那样,将您的图像视图和按钮放在容器视图中。如果子视图固定到容器的边缘,则不会遇到相同的居中问题(您可以相应地调整大小 - 高度为 360 + 30 + 70 == 460。然后您可以使用简单的 align-center-Y 约束。

  2. 您可以使用间隔视图。通过添加两个隐藏视图,您可以指定约束以将它们定位在图像视图和按钮的上方/下方,具有相同的高度,例如它们充当弹簧。这是使用您的尺寸执行此操作的代码:


- (void) viewDidLoad
{
    [super viewDidLoad];

    UIView* imageview = [UIView new];
    imageview.backgroundColor = [UIColor blueColor];
    imageview.translatesAutoresizingMaskIntoConstraints = NO;

    UIView* button = [UIView new];
    button.backgroundColor = [UIColor greenColor];
    button.translatesAutoresizingMaskIntoConstraints = NO;


    UIView* spacer1 = [UIView new];
    spacer1.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5];
    spacer1.translatesAutoresizingMaskIntoConstraints = NO;
    spacer1.hidden = YES; // comment out to show spacer!

    UIView* spacer2 = [UIView new];
    spacer2.backgroundColor = [[UIColor redColor] colorWithAlphaComponent: 0.5];
    spacer2.translatesAutoresizingMaskIntoConstraints = NO;
    spacer2.hidden = YES; // comment out to show spacer!

    [self.view addSubview: imageview];
    [self.view addSubview: button];
    [self.view addSubview: spacer1];
    [self.view addSubview: spacer2];

    NSDictionary* views = NSDictionaryOfVariableBindings( imageview, button, spacer1, spacer2 );

    NSArray* constraints;
    constraints = [NSLayoutConstraint constraintsWithVisualFormat: @"V:|[spacer1(==spacer2)][imageview(360)]-30-[button(70)][spacer2(==spacer1)]|"
                                                          options: 0
                                                          metrics: nil
                                                            views: views];
    [self.view addConstraints: constraints];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageview
                                                          attribute:NSLayoutAttributeWidth
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:nil
                                                          attribute:NSLayoutAttributeNotAnAttribute
                                                         multiplier:1
                                                           constant:300]];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button
                                                          attribute:NSLayoutAttributeWidth
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:nil
                                                          attribute:NSLayoutAttributeNotAnAttribute
                                                         multiplier:1
                                                           constant:210]];


    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:imageview
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1
                                                           constant:0]];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button
                                                          attribute:NSLayoutAttributeCenterX
                                                          relatedBy:NSLayoutRelationEqual
                                                             toItem:self.view
                                                          attribute:NSLayoutAttributeCenterX
                                                         multiplier:1
                                                           constant:0]];
}

回答by uchuugaka

You need to make your vertical spaces equal to or greater than on the top of the image view and bottom of the button. But, this also sounds like it would be easier to simply have on additional view that contains your image and button. Then center that view V and H in the super view.

您需要使垂直空间等于或大于图像视图顶部和按钮底部的空间。但是,这听起来也像是简单地拥有包含图像和按钮的附加视图会更容易。然后在超级视图中将视图 V 和 H 居中。

Auto Layout still means container views sometimes.

自动布局有时仍然意味着容器视图。