Search code examples
iosautolayoutstoryboardconstraints

How to setup constraints so a view's width is 0 if it can't be at least a certain width


I'm trying to create a launch screen storyboard (so no code) with a somewhat complicated layout.

The problem is that I need two views shown side by side (kind of like a split view controller). Here are the requirements:

  1. The left view's width should be half the width of the right view. In other words, the left view takes up the left 1/3 of the screen.
  2. The left view's width should be no more than 375 points. This takes precedence over the first constraint. In other words, the left view should try to take up the left 1/3 of the screen but be no wider than 375.
  3. The left view's width should be at least 340 points. If the width due to the first constraint would make it less than 340, it should be reduced to 0 and the right view should take up the full width of the screen. In other words, if the screen width is less than 1020, only the right view should be visible.

All three of these must be applied to the regular width size class.

I have the first two requirements working just fine. I can't figure out how to apply the 3rd requirement.

These are the constraints I have at the moment:

  • LeftView.top = SuperView.top
  • LeftView.bottom = SuperView.bottom
  • LeftView.leading = SuperView.leading
  • RightView.top = SuperView.top
  • RightView.bottom = SuperView.bottom
  • RightView.trailing = SuperView.trailing

Those pin the two views around the sides. The following additional constraints set the widths of the two views to meet my first two requirements:

  • LeftView.trailing = RightView.leading
  • LeftView.width = 0.5 * RightView.width @ 999
  • LeftView.width <= 375

With those in place you can see the results in various iPads.

On a 12.9" iPad in portrait you will see the left view taking up the left 1/3 of the screen and the right taking the right 2/3 of the screen. On a 12.9" iPad in landscape, the left view maxes out at 375 and the right view takes the rest of the screen. All good so far.

On a 9.7" iPad in landscape you see the left view taking the left 1/3 of the screen and the right view taking the right 2/3 of the screen. Again, good.

The problem I'm trying to solve starts with a 9.7" iPad in portrait. The left view is taking up 1/3 of the screen but it's really narrow. It's only 256 points wide. Since that is smaller than my desired minimum of 340, I don't want the left view to appear at all. The right view should fill the whole iPad.

Is there some combination of constraints, perhaps with various priorities, that can force the left view's width to 0 if it can't be at least 340?

As a side note, I have even different requirements for compact width but I have all of that working. It's just this one last requirement for different sized screens within the regular width size class that I need to solve.


Solution

  • Looks like I managed to achieve what you need. Tested it on iPad 9.7" and 12.9", works as expected. It's a bit of a mess, but it works.

    The trick is to use additional view, which should be 0 points wide if screen is less then 1020 points, and 1 point wide otherwise (I'll refer to it as OnePixelWideView). Then make your LeftView.width == 375 * OnePixelWideView.width @ 999.

    Another complication is that it will not work with LeftView.width = 0.5 * RightView.width @ 999, regardless of priority, but we can work around that by adding one more view with sides pinned to superview (I'll call it FullscreenView), and make LeftView.width <= 0.33 * FullscreenView.width

    Here is a full list of horizontal constraints:

    FullscreenView.leading = superview.leading
    FullscreenView.trailing = superview.trailing
    
    superview.trailing = OnePixelWideView.trailing + 1019
    superview.leading = OnePixelWideView.leading @ 999
    OnePixelWideView.width <= 1
    
    LeftView.leading = superview.leading
    LeftView.width <= 375
    LeftView.width <= 0.33 * FullscreenView.width
    LeftView.width = 375 * OnePixelWideView.width @999
    
    RightView.leading = LeftView.trailing
    RightView.trailing = superview.trailing
    

    Here is a .storyboard content to play around.