Set constraints in autolayout so UIView wraps visible children - ios

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.

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.

Now i am trying to set autolayouts so taht the mainView height is depended on its children
You cannot do this by constraints alone. Autolayout does not, in and of itself, normally size a view "from the inside out", i.e. by using its subview constraints. (The exception is for special self-sizing views like a scroll view's container view or a table view self-sizing cell.)
However, you can do it in code. This is what systemLayoutSizeFitting is for. You will have to perform manual layout on the superview, but you can do it easily by calling this method.

Each UI element requires 4 constraints to infer its bounds and position. The x position, y position, height and width.
Assuming you need to shrink the mainView to the height of the UILabels, set all the three constraints except the heightConstarintfor the mainView. ie, set constraints for x, y and width. Now set all the four constraints for the three child views. A constant value must be explicitly set for the heights of all the three subViews. Now the height for the mainView will be inferred from the heights of the child views. To wrap the labels, set the heightConstraint of the UIImageView to zero in code whenever required. An IBOutlet for the heightconstraint of UIImageView can be made to set it to zero.

Related

How to use auto layout to resize views in a table view cell?

I have a cell in which I place four buttons and four labels. Each button gets assigned a picture with width 50 and height 50. Furthermore, all buttons have a corresponding label describing what they're intended for.
My objective is to have the buttons and labels resize to keep the buttons' and labels' aspect ration intact while the screen dimension changes on different devices. I have been playing with auto layout changing the hugging and compression to achieve this but haven't been successful yet. Any help would be much appreciated...
I think you should take a look at a UIStackView, because this seems exactly as a use case for stack. Just put each pair button/label in a stack, and then all four pairs into a horizontal stack, which you constraint to the cell itself. You should be able to handle all you need just by configuring the stack’s properties (axis, distribution, alignment, spacing).
Embed your button and label into a view. Set the width of this view equal widths to content view and change the multiplier value to 1:4. This will adjust the widths of the views according to superview. Also, set the top and bottom constraint to 0 for this view.
Provide center align y-axis constraint to button after setting the width and height constraint to 50. Set its top constraint to a value you deem fit.
Set labels's leading and trailing constraint to a value like 8. Choose center alignment for text. Also, provide top constraint to buttona nd bottom to its superview.
Copy the view and paste to create the three views and provide them equal widths constraint to the first view. Also, provide their leading, trailing, top and bottom constraints.
Here are a fast tutorial in how to achieve that:
1-
2- completion of the first Gif:
Note you can achieve the same output using a UIStackView

How to make a view height grow depending on its child views contents

I have a parent view whose height has to be decided by its child content.
How may I achieve this in storyboard without programatically changing it.
You can accomplish this using AutoLayout.
Make sure each of the child views has constraints defining its size and position. Then, set the parent view's vertical content hugging and compression resistance priorities to required. This will define the parent's height based on the height and positioning of its child views.
Note that depending on what the child views are, you may want to change their vertical content hugging and compression resistance priorities as well. For example, a UILabel with numberOfLines set to 0 can automatically grow based on its content, so you'd want it to hug its content vertically and resist vertical compression so that it resizes the parent view.
This image shows the parent (white) view with its vertical hugging and compression resistance priorities set in the inspector panel. Notice that the parent view has constraints set for its width, x-position, and y-position, but not its height. It's able to infer its height based on the height and position of the child views (see the next image).
This image shows the constraints of each child view. Notice that the vertical hugging and compression resistance priorities of these views were not changed. Each of these views has constraints for x-position and y-position, but you'll notice that not all of them have constraints for width and height. Views like the label and switch are able to automatically infer their size constraints based on their content. If you don't set vertical position constraints on every one of the child views, AutoLayout won't know how much space each of them needs, so it won't know how tall the parent view should be.
1- Add you View container and add constraints. Don't set height nor bottom spacing or set it but with less priority (example 999).
2- Add items/things to your View Container and add constraints. Be sure to add all require constraints plus add bottom spacing to the bottom item inside.
That will define the height of the container View.
PD: Forget about Content Hugging/Compression and Priority. They are handy but no use here. They just set a resistance to get bigger or smaller.

How to use scrollview with subviews of a dynamic height?

I have a label inside a scrollview that has can be as few as 1 lines and as many as 10 lines. I'm having trouble figuring out how to make the scrollview content size dynamic so that it will stretch accordingly.
I tried adding a height constraint of >= 100 (arbitrary number) but then it complained about it being an Inequality Constraint Ambiguity.
Quick answer
Remove the current height constraint on your label.
Ensure the labels Lines property is set to 0 and Line Breaks is set to Word Wrap.
Add vertical spacing constraints to the views above and below the label.
Ensure that every view has vertical spacing constraints from the top to bottom margins, in order for the scroll view to infer the height of its contentView.
Explanation
In order for the scroll view to infer its content size it must have constraints from margin to subviews to margin - imagine it like a balloon the content is the air inside that pushes on the wall to make the balloon the size it is. The constraints from the subviews to margins allow the size of the subviews to push the walls of the content view out.
For the label setting the Lines property to 0 means it will have a variable amount of lines just as you want. The Line Breaks property being set to Word Wrap means it will ensure words are not cut off (truncated) or broken up into characters and instead pushed onto the next line as whole words.
If you don't specify a height constraint for a UILabel it will take a height that fits the whole text. Just make sure that number of lines is set to 0 and that your label has all the margin constraints set.
This tech note from Apple explains how to correctly configure a scrollview with scrollable content using auto layout only https://developer.apple.com/library/ios/technotes/tn2154/_index.html.
Conceptually, in your case, this is what you need to do:
View hierarchy:
MainView -> ScrollView -> ContentView -> UILabel
Scrollview is constrained to all edges of the main view,
ContentView is constrained to all edges of the ScrollView
and the UILabel is constrained to all edges of the ContentView (set UILabel number of lines to 0 and remove the height constraint you currently have applied)
The key here is to realise that the size of the contentView only depends on the size of the UILabel so as the UILabel height stretches so does the contentView. This will allow the scrollView to automatically infer the contentSize and enable scrolling if required.

How to simulate conditional hugging priorities in AutoLayout?

I have a UIView that contains a multiline UILabel and a UIImageView. The imageView is a square that is a specific height and width and is centered vertically in the cell, while the label is constrained to the top of the view. Here's a little illustration:
I want to be able to make the parent view expandable based on either the UILabel or the UIImageView, based on which one has the bigger height. How would I go about doing this (in Storyboard)?
You would set a bottom and top constraint for both the label and image view at a high priority. Set the constant equal to the amount of padding you'd want.
Then, on the parent view, you'll want to add a height constraint with a lower priority than the top/bottom constraints of the label and image.
That way, you guarantee padding between the label/image and their superview, which will force the superview to expand its height since its priority is lower than that of the padding.

Autolayout height equal to MAX(multiple view heights)

Say I have a view called container. container contains 5 UIButtons. I want to add a height NSLayoutConstraint on container, and this height should be equal to the NSLayoutHeightAttribute of the tallest button in its subviews.
I don't see a straightforward way to do this. Anyone have any ideas?
You need one constraint for each subview (button), specifying that the container's height should be greater than or equal to the subview's height. Give that constraint a high priority, like UILayoutPriorityRequired (which is the default anyway).
Then add one more constraint on the container's height, specifying that it should have a height equal to zero. Give that constraint a low priority, like UILayoutPriorityLow. Since auto layout tries to minimize the error of unsatisfied constraints, it will make the container as short as possible while still satisfying all higher-priority constraints.
I have put an example in this gist. It produces this result:
The blue views have fixed heights. The tan view is the superview of the blue views and its height is constrained as I described above. I pinned each subview's bottom to the container's bottom, but you could pin the tops or the Y centers instead.

Resources