iOS - Auto Layout issue with constraints priority and compression resistance - ios

I have been working on improving myself concerning Auto Layout and creating design.
I have read a lot about constraints priority, compression resistance and hugging priority but also intrinsic content size.
After re-creating almost all xib/storyboard files in my project, I have been struggling with a simple design of a custom UICollectionViewCell.
Here is the required design :
But I can't seem to create appropriate constraints in order to handle all screen resolution (specifically iPhone 5).
Here is what I have done :
I have create 2 separated views, title view at the top, and contentView.
Concerning the Blue and Gray Views :
Now in order to always have approximately same look on different screen device, I added a constraint equal height between the blue view and the contentView (white view), with constant 1:3. Which means the blue view will always be 1/3 of the parent white view.
And for the gray view, I have added an equalHeight constraint with the blue view equal to 1:2.
The UILabel and the 2 UIImage Views:
Now the fun part begin, I have setup trailing, top and bottom constraint for the UILabel in the blue view, and same but leading for the big ImageView which is outside, with horizontal constraints between the UILabel and the alert icon Image View, and horizontal constraint between the 2 Images Views.
Now I know I have auto layout constraint ambiguity because it doesn't know which element to compress or to clip.
EDIT : So I changed the content hugging priority of the UILabel to 1000 and now I have all my 3 elements but on an iPhone 5 the result is a catastrophe.
EDIT : Here is a global image with all the constraints. It doesn't look good on iPhone 5.

You have a constraint ambiguity because you have to set width/height constraints for the alertImageView and warningImageView, beside set contentHugging priority horizontal of the label to 1000
Also it's possible to create H spacing between the alertView and the warningView despite not being on the same container.

Related

Can't change the size of View Element in Xcode (Swift)

I am new to iOS Dev and Xcode.
I was trying to practice Auto Layouts in Xcode.
So I was faced with a problem where I wanted to have my score label 20 points from the left side of stack view. So I embedded my label in a UIview element and was able to constraint it to 20 points from left. However, it seems like my UIview element's size has been locked and I am unable to reduce its size.
I want my view element to take same height as my score label which is like 71.5 points but it turns out that minimum height that can be reached for this UIview element is 333.5 as you can see in the image above.
So, how can I reduce its height and did I do anything wrong?
----------- Updated: Here are the constraints -----------
You can delete the MyView.top = top constraint.
Your "Score:" label already has top, leading and trailing constraints to MyView, so add a bottom = Score:.bottom constraint. That will tell the bottom of the white MyView to stick to the bottom of the label.
You may also need to set the Score label's Content Hugging vertical priority to Required (1000).
That should do it.

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.

Equal width and equal spacing between buttons in autolayout

I'm trying to use autolayout constraints to automatically resize a few similarly-sized buttons in a view to give the following effect:
Before resizing
Desired effect after resizing
As you can tell, I want the buttons to be of the same size and I also want the spacing between each button to be a constant 20 points. It seems pretty simple at first, so I set the following constraints:
Buttons: space from left neighbour = 20 (inclusive of left-most and right-most buttons)
Buttons: space from right neighbour = 20 (inclusive of left-most and right-most buttons)
Buttons: same width
What actually happens after resizing
When in preview or when I test run the app in my iPhone/simulator, the button resizes and doesn't even follow the same width constraint I set for it. In fact, the view containing the views also resizes to fit the new button sizes. Anyone knows how to fix this problem purely in the interface builder?
Setting:
- equal widths of all buttons
- horizontal spacing between all buttons
- leading to superview for the first button and trailing to superview for the last button
should do the job. Unless you're having problems with the superview (e.g. ScrollView missing constraints)
In the interface builder you set the spacing constraints between buttons like you described above. Then you can command-select all of them and specify the "Equal Width" constraint to apply to the selected objects.
Finally I have oblivion how to solve this problem. I've test it works like charm.
add constraints to space items with 20 units margin
add constraint to ages
now tricky part
for each item add constraint equal widths to a parent
select all this new constraints and change its properties
set multiplier to value 1:5
set constant to -24 (6 separation between items and parent edge gives 120, this multiplied by multiplier value 1:5 gives 24)
update items frames
That's it! Picture below show how it works in interface builder:
Set simulated size to "freeform" and test different widths (I sett this to 330).
This problem is seems to be because of wrong content hugging priority and content compression Resistance priority. So you should set them as low content hugging and high compression resistance (all should have same value).
Because content hugging is the property that resist a view to grow and content compression Resistance priority is to resist a view to shrink. For more information regarding these you can found this Question.

Correctly Size UILabel inside UIScrollView using Interface Builder and Autolayout?

I've created a simple view setup in interface builder. Here's how it looks:
The view hierarchy is simply:
view
- scrollView
-- label
The scroll view is shown with grey background anchored to its super view top, leading, trailing, and bottom with constraints of 0.
The label is shown with yellow background and has constraints as shown. Additionally, the label has content hugging priority of 1000 for both horizontal and vertical, and it has content compression resistance priority of 1000 for both horizontal and vertical.
In portrait orientation, the label is sized correctly:
In landscape orientation, however, the label is not sized correctly horizontally (I intended for the label to fill the width of the screen, less constraint insets as shown):
How can I get this label to size correctly horizontally in landscape orientation?
There is one solution for you.
1. Add your UIScrollView to container (UIView) with zero constraints:
2. Add constraints for Label: top, bottom, leading, trailing spaces = 20.
3. Add constraint: label.width = container.width - 40
For this, select label in the view structure tree, tap ctrl and pull
to container. And select Equal Widths.
Then select the created constraint and go to its Utilities and set
the constant value to 40.
You should get the following components:
Run the app, go to landscape and it works!
Hope it is clear. Best Regards.
It's hard to tell which constraint(s) to add/remove in order to get what you want because iOS reserve the right to adjust your constraints whenever it becomes impossible for it to satisfy all your constraints.
I make a blank project with the same view you have (UILabel as subview of UIScrollView) and make some constraints to get the UILabel resized properly on landscape.
A must check though:
Make sure you set the vertical/horizontal spacing constraints from the pin option, as shown below and try to remove unneeded constraints manually.

iOS Auto Layout >> View is not Changing its Size

I have a design for a screen that should look like this (other things will be added later, but I cannot seem to resolve the basis...):
I have added Constraints to determine the following:
Both Labels are Constraint in spacing to the screen edges.
Middle View is Horizontally and Vertically Constraint to the Middle of the Background View Center.
I have added 4 Constraints to express Minimum and Maximum Vertical Spacing between the Middle View and the Labels (Current spacing as Maximum and Standard spacing as Minimum).
I have also added 2 Constraints to the Middle View to define Spacings from the Screen right and left edges.
I thought that it should be enough, but in reality, when switching between Retina 3.5 and 4 the Bottom Label disappears and the Middle View is cut in the middle:
I have tried lowering the Middle View Content Hugging and Content Compression Priorities, and still no good.
Here are the Warnings I get:
Any idea how to resolve this?
Or alternatively, how to approach it differently (preferably, still using Auto Layout)?
Add Equal Width & Equal Height constraints as well & It will work
Add TopSpaceToContainer constraint for Top Label. Then add width and height constraints for your yellow view at the middle. Remove the multiple vertical spacing constraints given to the Top Label and Bottom Label.

Resources