AutoLayout to keep view sizes proportional

60,063

Solution 1

This can be resolved by adding one more dummy view(dummyView) with its constraints set to Fixed width, height and aligned to centerX of Superview.Then add left view and right view Horizontal spacing constraint to dummyView.

enter image description here enter image description here

Solution 2

I'm probably late in coming up with a solution but this can actually be made very easily in IB.

First, add a UIView and pin in to all four edges of the superview.

Then, Add your first subview and position it accordingly (ig : x = 0, y = 0, height = fixed height, width = The width you would like relative to the UIView we pinned to all four edges).

Select both the UIView and the first subview and add an Equal Widths constraint. Of course, this will show you an error in positioning in the autolayout, but that's OK because this is not (yet) what you want.

Now comes the trick : select the Equal Widths constraint and edit the Multiplier to be the ratio you want (eg : 1:4 if you want the first subview to be 1/4 of the UIView). Repeat steps for the second subview and Tadaaaaaa !

enter image description here

Solution 3

I'm new to autolayout but came across your question and thought it would be a good challenge. (That's my caveat in case this isn't the ideal solution!)

You'll need to add the width constraints in code. I achieved this by firstly adding the two views in the NIB without width constraints. These are the constraints for the first (left) view:

enter image description here

These are the constraints I had for the second (right) view:

enter image description here

This leaves an extra constraint you don't want on the second view - leading space between superview and the second view as shown below:

enter image description here

You can't remove that constraint in IB as it would leave an ambiguous layout (as we don't have widths on the subviews). However you can remove it in code. Firstly, set up an outlet for it and connect it in IB:

@property (nonatomic, strong) IBOutlet NSLayoutConstraint *view2superviewLeadingConstraint;

Then, in your view controller's viewDidLoad, you can remove it using:

[self.view removeConstraint:self.view2superviewLeadingConstraint];

Finally, add the width constraints. The key here is the multiplier parameter to dicate what percentage you want the widths to be based on the superview width. Also note that you have to set the constant parameters to equal the leading/trailing totals set up in IB:

NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:self.view1 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.3 constant:-20];
[self.view addConstraint:constraint1];

NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:self.view2 attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:0.7 constant:-40];
[self.view addConstraint:constraint2];

Solution 4

Just select the aspect ratio :

enter image description here

Solution 5

As others have mentioned, the key is setting a multiplier on the "Pin Widths Equally" constraint so that the views widths end up being a multiple of each other. XCode 5.1 should add the ability to set this in Interface Builder, but until then you've got another option besides setting it in code. If you create the "Pin Widths Equally" constraint, then go to the Identity Inspector in the Utilities Panel on the right, then look for "User Defined Runtime Attributes". Add a new attribute with key path "multiplier", type "number", and value equal to your desired ratio.

Multiplier set as a runtime attribute, as described above.

This won't be reflected in Interface Builder, but will apply when your view is used.

Share:
60,063
Simon Germain
Author by

Simon Germain

I'm a software developer for Turner Broadcasting System, Inc. for the Enterprise Applications department. I develop mainly iOS applications but also C# .NET applications. In the past, I used to work as a PHP developer, working for an advertisement agency called 22squared. I have a blog, on which I almost never write, sadly. When I first started, I was tried to keep myself motivated about blogging there at least once a week, talk about some stuff that I do for my work and things I discover or want to share with people.

Updated on May 03, 2020

Comments

  • Simon Germain
    Simon Germain almost 4 years

    I'm trying to achieve the following:

    • I have 2 views in my xib that need to stay 20 pixels off the edge (both sides and top)
    • The 2 views that need to resize aren't the same size
    • They have to be 20 pixels apart
    • Their width needs to stay relative to the width of the parent view

    I read a tutorial about doing just that and it works but the problem with it is that it requires both views to have the same width and pin Widths equally, which I don't want.

    Here's what I tried:

    • Add leading space constraint to left view to be 20 pixels
    • Add top space constraint to left view to be 20 pixels
    • Add top space constraint to right view to be 20 pixels
    • Add tailing space constraint to right view to be 20 pixels
    • Add horizontal spacing constraint to both views to be 20 pixels

    The problem I'm running into is that the left view doesn't resize and the right view fills out the space to keep the 20 pixels horizontal space.

    Is there a way I can get both views to resize proportionally to the space they should be filling?

    Here are the screenshots of my layout and constraints:

    xib LayoutConstraints definitions

    Thanks!

    EDIT

    I get the following warning when I try to rotate my device:

    2012-10-11 08:59:00.435 AutolayoutTest[35672:c07] Unable to simultaneously satisfy constraints.
        Probably at least one of the constraints in the following list is one you don't want.
        Try this: (1) look at each constraint and try to figure out which you don't expect;
        (2) find the code that added the unwanted constraint or constraints and fix it. (Note:  
        If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
    (
        "<NSLayoutConstraint:0x8a6b2b0 H:[UIView:0x8a6b1d0(170)]>",
        "<NSLayoutConstraint:0x8a68ad0 H:[UIView:0x8a69430(90)]>",
        "<NSLayoutConstraint:0x8a6ba40 H:[UIView:0x8a69430]-(20)-[UIView:0x8a6b1d0]>",
        "<NSLayoutConstraint:0x8a6ba00 H:[UIView:0x8a6b1d0]-(20)-|   (Names: '|':UIView:0x8a6b7e0 )>",
        "<NSLayoutConstraint:0x8a6b940 H:|-(20)-[UIView:0x8a69430]   (Names: '|':UIView:0x8a6b7e0 )>",
        "<NSAutoresizingMaskLayoutConstraint:0x7199aa0 h=--& v=--& V:[UIView:0x8a6b7e0(568)]>"
    )
    
    Will attempt to recover by breaking constraint 
    <NSLayoutConstraint:0x8a6b2b0 H:[UIView:0x8a6b1d0(170)]>
    
  • Simon Germain
    Simon Germain over 11 years
    I tried making those 2 width constraints optional and the warning is gone, but the problem with one view resizing while the other doesn't remains.
  • Matthew Lowe
    Matthew Lowe over 11 years
    Try making the width constraints "greater than or equal to" rather than "equal to".
  • capikaw
    capikaw about 10 years
    You do not need to remove constraints directly. In IB, select the 'placeholder' constraints one at a time (click the cog-down-arrow and choose 'Select and edit...') then check the 'remove at build time' box.
  • atxe
    atxe almost 10 years
    As you said Interface Builder (XCode 5.1) includes the multiplier factor for Equals Height constraints :) Accept this answer! :)
  • Simon Germain
    Simon Germain almost 10 years
    I like the idea! Let me try it out!
  • nvrtd frst
    nvrtd frst over 9 years
    Fortunately it seems in XCode 6, they have the "aspect ratio" constraint" which solves this problem
  • DeFrenZ
    DeFrenZ about 9 years
    Why not constrain the sides of the two views to the center of the superview directly then?
  • Yatheesha
    Yatheesha about 9 years
    @DavideDeFranceschi If we make like You said both the view may collapse each other because the trailing constraint from left view to right view is missing.
  • DeFrenZ
    DeFrenZ about 9 years
    Not sure about that, each view will have 2 independent horizontal constraints and so they will be well defined. Of course if the container view is too small (so much that one of the view would result in negative width) then it won't work, but that shouldn't be something happening in the study case
  • Jessica
    Jessica almost 9 years
    How can I change the multiplier programmatically, with fractions?
  • Benjamin
    Benjamin almost 9 years
    @Jessica : The multiplier property is read only. You have to remove the old NSLayoutConstraint and replace it with a new one to modify it. This may seem like a nuisance but it's Apple design. You have no other choice. Sorry.
  • Jules
    Jules over 8 years
    How would you apply this to spacing? I love this idea. +1
  • Benjamin
    Benjamin over 8 years
    @Jules : what do you mean by applying it to spacing?
  • Jules
    Jules over 8 years
    Pinning to an edge, think I can just installed different layouts for different constant values.
  • Benjamin
    Benjamin over 8 years
    OK. I see what you mean. No you cant' do that because the multiplier is relative to two views.
  • Lane Rettig
    Lane Rettig over 7 years
    Aspect ratio of what? How does this solve the problem?