Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Center two views vertically using NSLayoutConstraint

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

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).

I cannot make this to work using NSLayoutConstraint.

So far, what I did, has been centering the X coordinate of the two elements using NSLayoutAttributeCenterX and NSLayoutRelationEqual to the same view attribute.

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.).

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.

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?

like image 245
Fabiano Francesconi Avatar asked Jun 25 '13 10:06

Fabiano Francesconi


1 Answers

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.

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

like image 155
Fabiano Francesconi Avatar answered Nov 05 '22 18:11

Fabiano Francesconi