How to Constrain a Square View with Auto Layout in iOS

Using Auto Layout, what initially seems easy can be more complicated. Auto Layout always requires you to be very explicit. This can be an extremely powerful thing in cases where you may want a greater level of control. On the other hand, constraining simple views can require a lot of constraints.  For example, I may want a view to be a perfect square and be as large as possible in its container view whether the device is rotated in portrait or landscape. To demonstrate, consider these two examples:

Final square portrait

Final square landscape

You can see the purple area of the screen is a separate UIView that I want to be as large as possible and also still be a perfect square. This is how I want the view to behave when I rotate the device to portrait and landscape.

Initial Attempt with No Regard for Rotation

If my app only worked in portrait mode, then the constraints I need would be very simple. The width of the square is the limiting factor when only drawing in portrait mode.

To tell Auto Layout how large the square should be, all I need to do is pin the left and right edge of my purple view to the left and right edge of my containing view. Next I center the purple UIView horizontally and vertically within the container. Finally I apply a 1:1 aspect ratio on the UIView which constrains the view to be a perfect square. Below is what the final constraints look like in Xcode.

Initial square constraints

Initial square constraint list

This works fine in portrait mode, but look what happens when I try to rotate the device:
Initial square rotation

When I rotate the device, Auto Layout cannot satisfy two of my constraints: one that specifies that the purple view be square (1:1 aspect ratio) and the other that pins the left and right edge of the square to the super view (specifying the size of the square).

A Square View that Responds to Portrait and Landscape

The first two constraints that I need to remove, from my previous example, is the left and right edge constraints. These will not work in landscape mode because the view is now constrained in the vertical direction not horizontal. I need a way to tell the purple UIView how big it needs to be.

The only way to handle this situation is to use constraints with different priority. What this does is basically tell iOS which constraints to break first when two are conflicting. I am going to setup the constraints again, but I will use two different priorities for the constraints.

Required Priority Constraints (value = 1000)

The following constraints cannot be broken in portrait or landscape mode:

  1. Align Center X (center view in container)
  2. Align Center Y (center view in container)
  3. 1:1 Ratio to container view (make the width and height equal or square)
  4. Width of the purple view has to be less than or equal to the container view
  5. Height of the purple view has to be less than or equal to the container view

High Priority Constraints (value = something less than 1000)

The following constraints are not required, but Auto Layout will try to abide by them. Their value can be set to any value less than 1,000. I set my priority to 750 because that was a convenient option to pick from the dropdown list in interface builder. What these constraints do is maximize the size the square to be the larger of the width or height of the containing view (whichever is smaller).

  1. Width of the purple view is equal to the containing view
  2. Height of the purple view is equal to the containing view

Below is what the final constraints look like in Xcode:

Final square constraints

Final square constraints list

Notice that the two “Equal Width/Height to: Superview” constraints have a dotted line to indicate they are not required constraints.  Below is the detail of the equal height constraint with a priority of 750:

Lower priority height constraints

Final Result

Below is the view behaving correctly in portrait and landscape:

If you would like to look at the constraints yourself, I have the entire project on GitHub.

Conversation
  • Victor Baro says:

    Hello Mike,
    Great tutorial.
    I am not a fan of low priority constraints.. They look a bit messy with dotted lines (personal preference).
    I am not in front of Xcode, but I think you could achieve the same result by having view.width <= superview.width (same for height), and center alignment in superview for X and Y.
    Another way would be by using 'new' iOS 8 (funny to say new 2 days before iOS 9..) size classes, so you set your constraints in portrait and landscape independently.
    Anyway, is nice to see someone using low priority constraints. I haven't seen many tuts out there explaining them.
    Best,
    Victor.

  • Ace says:

    Hi Mike,

    Found your tutorial because I was looking to solve a similar problem (except in my case the square has to be top left instead of centered).
    My problem is, it works for a while, until I add other views. Then suddenly the square disappears. Because you only specify maximum width/height. So the constraints are satifsfied with w:0 h: 0. How to make sure it fills as much as possible?

  • venkat says:

    If I don’t set constraints at all. I can achieve the same I believe. Sorry if I am wrong

  • Jordan says:

    Just what I was looking for! Thank you!

  • Abdulkadir says:

    Just awesome….

  • David Reich says:

    Excellent!
    I have a square view (this would also apply to other aspect ratios)
    that I need to inset inside a larger UIView.
    The orientation of the containing UIView can be either landscape or portrait.
    This depends upon the visibility of other views (i.e. expands to fill space) which is determined at run-time. The size and orientation varies with device sizes.

    This takes care of that perfectly.
    Otherwise I was going to have to do this programmatically.

    Thanks!

  • Comments are closed.