Search code examples
iosobjective-cautolayout

Set constraints in autolayout so UIView wraps visible children


I am trying to achieve the following.

I have a

--> MainView
      --> UIImageView 200x200
      --> UILabel W:200(max) , H: Variable 
      --> UILabel W:Variable , H: 20 

All the views in mainView are placed in sequence one after another. Now i am trying to set autoLayout so that the mainView height is depended on its children,

E.g If i set ImageView hidden then it should wrap both UILabels etc.

How can I set autoLayout constraints so that the mainView have "Wrapping" effect over its children.


Solution

  • The easy way to achieve this is using a UIStackView (WWDC 2015 session video). Pure Autolayout is a lot more complicated in this case.

    Assuming you would like to lay the children out vertically, left-aligned:

    Controlling the Trailing Edge

    Add a greater-than-or-equal 0 constraint between the trailing edge of each child and the parent's trailing edge. This will cause the widest child to push the parent's trailing edge to the right. These constraints should have a very high priority.

    You will need another constraint to prevent the layout from being ambiguous. With the three trailing constraints the width of the parent is ensured not to be smaller than the widest subview. You also have to constrain the parent's width not to be greater than the widest subview's width. Just add a width constraint to the parent with a constant of 0 and a very low priority.

    I like to think of that low-priority width constraint to work like a rubber band trying to pull the trailing edge as far to the left as it can. The greater-than-or-equal-to-zero constraint of the widest subview prevents it from pulling any further.

    Hiding views does not have an effect since hidden views still take part in the layout calculation. You will need to keep a references to the greater-than-or-equal constraints and disable the corresponding constraint when hiding a child to take it's trailing edge out of the equation.

    Controlling the Height

    The heights of the children are likely defined by the view's intrinsic content size. Conceptually the Autolayout engine adds width and height constraints to the view according to the settings for content hugging and content compression resistance.

    There will be two hidden height constraints for views that have an intrinsic height dimension: one for content hugging and one for content compression. Hugging constrains the height to be less than or equal to the intrinsic height. Compression resistance constrains the height to be greater than or equal to the intrinsic height. The height of the view is exactly equal to the intrinsic height if both can be fulfilled. The priorities for content compression resistance and for content hugging can be set separately for fine-grained control over when which constraint breaks.

    We can use this knowledge to let the parentView's height shrink if a child is hidden. We need a "rubber band constraint" for the parent's height:

    Constrain the height of the parent to zero with a low priority, say 2.

    Whenever you hide a view, make sure to lower the vertical compression resistance priority of that view to a value less than the rubber band constraint priority, say 1. Now the rubber band overpowers the compression resistance constraint, causing the height of that view to collapse and the parent to shrink accordingly. Be sure to raise that priority to a value greater than the rubber band constraint when un-hiding the view to reverse the effect.