How to simulate conditional hugging priorities in AutoLayout? - ios

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.

Related

Setting the constraints of a UI Collection View Cell

In my UI there is a collection View where each cell contains a UILabel and a UIImageView. There are aligned as follows.
Where I am stuck is setting constraints to them because each UILabel has a random length. I always want to make the distance between left side of the image and left side of the cell equal to the distance between the right side of the label and the right side of the cell (in a certain cell).
I know I can set the centerY of each element equals to centerY of each element. But how can I set the other anchor.
I am not Using Storyboards.
I would add a horizontal stack view, set constraints to every edge with the desired margin, and add the image and label to the stack view.
Then set the hugging priority to the image view higher than the label. This will cause the label to take up the remaining space while being aligned to the left.
If I understand your problem correctly, you want to set spacing for left side of UImageView and right side of UILabel equally. For this, I would recommend you to put those views (UIImageView and UILabel) in a UIStackView with properties: axis as "Horizontal" and Alignment as "Center"; and adding constraint for Leading and Trailing Space with CenterX and CenterY Constraint.
For ease of understanding take a look at the image of UIStackView Constraints

Unable to keep a fixed size of collectionview cell when adding constraint to it's subview i.e. a UILabel

I have a horizontal collection view that contains an UILabel. I have mentioned the collection view cell's size in the delegate method.
And added leading, trailing, height and vertical center constraints to that label as shown in the below image.
But in the output, the width of the label is getting increased or decreased based on the text and the collection view cell size as well.
I want to keep the size of the collection view cell fixed as mentioned in the delegate method.
How can I accomplish this?
Two things.
First, it sounds like you might be adding a width constraint to the label that’s equal to the label’s text width. That width constraint will force the cell to expand to meet the requirements of the leading, trailing and now width constraints. Instead, make the width constraint equal to the width the label should be.
Second, set the label’s .numberOfRows property to 0. This will make it so the label will break into multiple horizontal lines instead of going wider.
Edit:
Another thing that could help might be to adjust the horizontalCompressionResistance property on the label to 249 or something lower.

How do I make the UIView superview of a UILabel fit the intrinsic height of the UILabel using the storyboard only?

I got a UIView whose child is a UILabel. I then aligned the left, top, right, and bottom of the UILabel to that of its superview (the UIView) using constraints.
But what I'm getting is a UILabel that matches the height of its superview but not its intrinsic height (smaller or larger than its text content depending on the size of the UIView). What I expect is for the UIView to resize itself to fit exactly the height of the UILabel.
So how do I do this using only the interface builder?
In order to make the UILabel keep its size and force the outer view to resize you can update the values for Content Hugging Priority to 1000 (a.k.a. Required). This can be done in the measurements panel when selecting the label in Interface Builder.
I think they are set as a default to 750.
This should (if there is nothing else causing the change) make the label take its intrinsic content size and force its superview to conform to that size also.
Align top, trailing, bottom and leading of UILabel with UIView (add constraints).
Remove height and bottom constraints from UIView if any.
If the content is large, you probably need to wrap UILabel into a UIScrollView in place of UIView. and add a bottom constraint to UIScrollView
Content Hugging Priority of UILabel (251) is more than that of UIView (250) by default. Verify this
It's super easy using auto-layout. Just, follow these steps
1 - Drag a UIView and align it vertically and horizontallyin centre.
2 - Now drag UILabel into the UIView and align the label also horizontally and vertically in centre.Now, the IB aligns the label at the centre WRT to the superview and not the UIView.So, change that in the size-inspector section.
3 - Once you have done that and all red lines are removed, select both the label and the UIView together using the command key.
4 - Now, give them constraints as follows
leading = 0, trailing = 0 and select the equal width and equal height.
There you are done.If you wanna test that the UIView size is respective to the label's content, try increasing the label's font to a bigger size and you will see that the size of the view will be the same as the size of the label.
There , you are done :-D. Hope, this was helpful.
If someone is experiencing this issue, while:
Having 2 labels inside the UIView
Doing everything what was suggested here
In my case, I set Vertical Content Hugging Priority of BOTH UILabels to 1000. Then one of the labels had a different font. (ie. one label was supposed to be smaller than the other one)
The height was being ambiguous, because both labels were trying to force their height on the superview. Once I lowered the priority on the smaller label, everything worked fine.

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.
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.

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.

Resources