141

have been getting into autolayouts recently and I'm stuck on what seems like a really trivial problem example. I have a view that I want to sit at the top of the screen, and take up half of the screen-height. Simple before autolayout - just tack it in place and tell it to expand vertically when the superview resizes.

Now, I can't for the life of me see how to do it. Here's what I get when I try to set this up:

autolayout blues

The bottom space constraint is set to "equals 284", which is absolute and absolutely useless to me when I change to iPhone4 layout, as it keeps 284 points space at the bottom of the screen, and shrinks the view to no longer be half the size of the screen. And there's no way of setting that constraint to equal some fraction of any other view's height..

After struggling for a while, the only way I can think of doing this would be to introduce another view below this view, pin their heights equally, have them sit above and below each other and then set the second (bottom) view to be invisible.. which seems a bit ugly!

Am I missing something obvious?..

Firo
  • 15,448
  • 3
  • 54
  • 74
Mete
  • 5,495
  • 4
  • 32
  • 40
  • 1
    I don't think it is ugly, but a smart trick to get what you want using IB. I have done this, and when you use proper element names, you can make clear what is going on. You can even have a third element on a different position, or centers, change size based on these two subviews. – Jelle Dec 19 '13 at 12:36

6 Answers6

193

Storyboard solution where you can set exact ratio between any views:

Set height equality constraint

Now:

Edit constraint's multiplier

PROFIT!!!

Result

P.S. Also note that this method works with views on different nesting levels and (obviously) applicable for width

P.P.S. sometimes it might be helpful to "reverse first and second item" of constraint or set reverse multiplier (for example 2 instead of 0.5) (but these methods are not helpful if you don't understand how views relate between each other).

Fyodor Volchyok
  • 5,610
  • 4
  • 28
  • 45
  • 1
    Step 2 doesn't seem to work for me. I can't select anything in the panel except toggling 'Constraint to margin'. I'm trying to set the width of a UIImageView in a UITableViewCell to 50% of the content view. – DrMickeyLauer Dec 29 '14 at 17:15
  • 2
    UPDATE: Obviously interface builder does not allow the content view to be the explicit target of any auto layout constraints. I have to insert a dummy subview as child of the content view to make this work. – DrMickeyLauer Dec 29 '14 at 19:54
  • 1
    It works for me. You need to select the two views in the listing not the builder screen. for percentages divide the smaller view by the larger view to get the multiplier value example smaller view 25, larger view 135 - 25 / 135 = 0.185. Set this as the multiplier value to get a view that at x1 and x2 = 25 but scales up with larger 6 and 6 plus etc – latenitecoder Jan 29 '15 at 22:22
  • 1
    This worked for me, but please note that I had to also add three constraints that set the subview's margin constraints to 0. Thanks – raddevus Oct 15 '16 at 23:52
  • This should be the accepted answer. First I try accepted answer. It didn't work then I scroll down and I saw this answer and it worked. Thanks! – Mihir Oza Mar 13 '19 at 13:26
  • I know this answer is really old. But you are setting your height's constraint constant to zero. That doesn't make sense. – Houman Sep 18 '21 at 09:23
  • @Houman, it does, you could learn more about constraint math here https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html. In this case non-zero constant will introduce unneeded bias, thus we care only about multiplier here. Try it yourself in interface builder! – Fyodor Volchyok Sep 18 '21 at 14:11
175

This is now possible in IB as of [at least] Xcode 5.1.1. Although it took me sometime to figure out it is actually super simple:

First create a basic top alignment constraint (you will also need to setup bottom, left, and right constraints, like normal) . Then select the constraint and navigate to the Attribute inspector:

Demonstration of steps above

Then you can adjust the multiplier. If you want it 50% of the super view leave it at 1, since it is aligned per the super's center. This is also a great way to create views that are other percentages too (like 25% of super view)

Demonstration of second step above

Almas Adilbek
  • 4,371
  • 10
  • 58
  • 97
Firo
  • 15,448
  • 3
  • 54
  • 74
  • nice :) I think this post should be marked as accepted answer :) iOS Developer often prefers Interface Builder Solution rather than code-like solution – hqt Sep 13 '14 at 19:14
  • @hqt, Thanks. I think when the accepted answer was written this method was not available. Sometimes askers do not revisit old questions or care to change the accepted answer. But ultimately I am just here to help out, glad it did! – Firo Sep 13 '14 at 19:46
  • 3
    I'm a bit confused. I tried this but it just centers the view to the superview, it doesn't adjust the view's height. – netwire Oct 31 '14 at 14:57
  • @Dean, you need to make sure that the `First Item` is `View.Top` not `View.Center Y`. Otherwise, yes it will just center it. You will also need to make sure other constraints are set properly to handle the view's other edges. – Firo Oct 31 '14 at 17:22
  • 1
    View.Top is Equal to Superview.Center Y multiplier 1 gives you no height. This answer is incomplete. All you have to do is think about what you're saying and you can see it gives you a line across the center! I would edit this answer to make it clear what other constraints you're talking about. – SmileBot Mar 23 '15 at 18:57
  • It does if your other constraints are setup properly. If your bottom is pinned in the proper place (the bottom) then it would have exactly half the superview. I'll edit the answer with this info. Thanks. – Firo Mar 23 '15 at 19:03
31

After a bit more time I've come up with the following.

I'm noting it as an answer but it's not very satisfying, since it assumes you can't actually do this in Interface Builder, but the correct constraint can be added in code afterwards as:

- (void) viewWillAppear:(BOOL)animated
{

    NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:upperview
                                                                 attribute:NSLayoutAttributeHeight 
                                                                 relatedBy:0 
                                                                    toItem:self.view
                                                                 attribute:NSLayoutAttributeHeight
                                                                multiplier:.5 
                                                                  constant:0];
    [self.view addConstraint:constraint];

}

Basically, it sets a multiplier of 0.5 against the height of self.view, acting on upperview. I had to set the priority of the bottom vertical space constraint in IB to lower than 1000 to avoid a bunch of runtime messages about breaking constraints as well.

So if anyone can show how to do this in Interface Builder, that would better answer my question, otherwise I guess this is as good as it gets (for now)???

Matteo Gobbi
  • 17,697
  • 3
  • 27
  • 41
Mete
  • 5,495
  • 4
  • 32
  • 40
  • 3
    I think this is as good as it gets now. It would be nice if Apple gave us access to the multiplier value in IB, so that could be set as well as the constant. – rdelmar Feb 18 '13 at 16:48
  • 3
    For anyone looking at this answer, Xcode 5.1 now allows you to set the multiplier via Interface Builder. I am now able to configure views that are half the height of their superview all via IB. – Mark Krenek Apr 05 '14 at 14:18
11

Slightly easier and more straight forward than method than Fyodor Volchyok's answer. -Hold down the control button and click on the subview. -Still holding down on the command button, drag cursor to the superview then click on the superview. -Select "Aspect Ratio".

enter image description here

-Then click the Size Inspector. -Then double click on the constraint. enter image description here

-Make sure that "height" is selected for both items. enter image description here

-Then change the "Multiplier" to 0.5 for half the screen, or whatever fraction of the superview you desire. enter image description here

user444333222
  • 151
  • 1
  • 4
7

There i also another possibility in code in case you have 2 views that should both have the same height: just set the height of view2 to the height of view1 (the trick here is not setting the height of view1 explicitely).

    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[topLayoutGuide]-0-[_view1]-0-[_view2(==_view1)]-0-[bottomLayoutGuide]"
                            options:0
                            metrics:nil
                              views:viewsDict]];
brainray
  • 12,512
  • 11
  • 67
  • 116
  • Didn't realize you could reference other views for widths/heights like that. This is the cleanest answer IMO if you're already in the visual format language. – loeschg Sep 05 '14 at 14:47
  • @brainray example is also explained at [developer.apple.com here](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html#//apple_ref/doc/uid/TP40010853-CH5-SW7) – alecs.popa Mar 12 '15 at 23:09
0

Swift version of Mete's answer:

    override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.



    upperView.translatesAutoresizingMaskIntoConstraints = false


    var constraint = NSLayoutConstraint(item: upperView,
                                        attribute: NSLayoutAttribute.top,
                                        relatedBy: NSLayoutRelation.equal,
                                        toItem: self.view,
                                        attribute: NSLayoutAttribute.top,
                                        multiplier: 1,
                                        constant: 0)

    self.view.addConstraint(constraint)


    constraint = NSLayoutConstraint(item: upperView,
                                    attribute: NSLayoutAttribute.height,
                                    relatedBy: NSLayoutRelation.equal,
                                    toItem: self.view,
                                    attribute: NSLayoutAttribute.height,
                                    multiplier: 0.5,
                                    constant: 0)

    self.view.addConstraint(constraint)


    constraint = NSLayoutConstraint(item: upperView,
                                    attribute: NSLayoutAttribute.leading,
                                    relatedBy: NSLayoutRelation.equal,
                                    toItem: self.view,
                                    attribute: NSLayoutAttribute.leading,
                                    multiplier: 1,
                                    constant: 0)

    self.view.addConstraint(constraint)


    constraint = NSLayoutConstraint(item: upperView,
                                    attribute: NSLayoutAttribute.trailing,
                                    relatedBy: NSLayoutRelation.equal,
                                    toItem: self.view,
                                    attribute: NSLayoutAttribute.trailing,
                                    multiplier: 1,
                                    constant: 0)

    self.view.addConstraint(constraint)


}
user444333222
  • 151
  • 1
  • 4