I want to have the bottom view be at least 20 away from both the image and the label above it. The label is multiline, so it can be taller or shorter than the image view, depending on how much text there is. When I add two "distance to nearest neighbour >= 20" constraints like shown in the screenshot, Xcode tells me constraints are ambiguous.
How do I fix it?
I think you can do it like this, if I understand your requirements:
The image view has a fixed width and height, and constraints to the left side and top, as well as a constraint to the bottom view of =20 with a priority of 700. That's crucial -- that will set the y position of that bottom view (which has fixed height and constraints to the two sides), but will allow it to move lower if another constraint with higher priority makes it. That constraint with higher priority is the constraint to the label -- it's >=20 with priority of 1000 (the label also has constraints to the top, right side, and trailing edge of the image view).
Related
Not sure where one needs to use Dynamic height label and a text field next to each other and
how the constraint recipe works using a required, greater-than-or-equal constraint and an optional constraint to set the vertical spacing of the controls based on the tallest control at runtime?
Taken from Apple Docs -
"This recipe dynamically sets the vertical spacing of the controls
based on the tallest control at runtime. If you increase the label’s
font size to 36.0 points, then the layout’s vertical spacing is
calculated from the top of the label instead. If you increase the font
size of a label, you would typically also increase the font size of
the text field. However, given the extra large fonts available through
the iPhone’s accessibility settings, this technique can prove useful
when mixing dynamic type and fixed-sized controls (like images)."
Reference: https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/ViewswithIntrinsicContentSize.html#//apple_ref/doc/uid/TP40010853-CH13-SW8
Solution screenshot:Views with/without constraint recipe for Dynamic height label and a text field
Even though your question is a little unclear, let's see if this helps explain things...
Suppose we start with 2 labels - LabelA (green) and LabelB (cyan):
LabelB is constrained 20-pts below LabelA... as we add text to LabelA, it will grow in height, "pushing" LabelB down and keeping the 20-pts vertical spacing:
All of that is pretty simple, and it's exactly what we expect to happen.
But, suppose we want LabelB to start at 100-pts from the top of the view (the safe-area), and only move down if LabelA gets tall enough?
If we add a Top constraint to LabelB, that will stretch LabelA:
Or, it will compress LabelA:
In both cases because LabelB Top cannot be both 100-pts from the view Top AND 20-pts from LabelA Bottom at the same time.
So, let's change LabelB Top to at least 20-pts from LabelA bottom, by setting it to >=, and, we'll change the LabelB Top constraint to a priority of less-than-required. In this example, we'll use Default High (750).
What we've done is told auto-layout to break the 100-pt Top constraint if needed. So, if LabelA is short, LabelB can be at 100-pts, but if LabelA gets tall, keep LabelB 20-pts below LabelA and break the 100-pt Top constraint:
So, it's not so much the order in which the constraints are evaluated, as it is the logical ability for auto-layout to satisfy all the constraints.
Edit
To try and explain the specific example from Apple...
Let's start with a minimum set of constraints, where the fonts are:
Label - 30pt
Field - 14pt
The Name Label is 40-pts from the Top, and the Name Text Field is constrained to the FirstBaseline of the label.
Now let's change the fonts to this:
Label - 20pt
Field - 30pt
so the Field is taller than the Label, here's what we get:
The Top of the label is still 40-pts from the Top, but the field has been moved up (to maintain the baseline alignment) and the Top of the field is now only (about) 25-pts from the Top.
The goal is to keep the Top of tallest element at 40-pts. With only these two vertical constraints, as we can see, we failed.
So, let's reset the fonts to:
Label - 30pt
Field - 14pt
and add the additional constraints with specified Priorities:
The positioning is identical to the first example, but... if we again change the fonts to:
Label - 20pt
Field - 30pt
this is the result:
The Top of the field is at 40-pts, and the Top of the label has moved down (to maintain the baseline alignment).
And... we've accomplished our goal!
Edit 2
To try to clarify with "plain language"...
The Baseline constraint will control the relative vertical alignment of the two elements. As we change the font size of one, it will make that element "taller" (or "shorter") and the other element will move vertically to keep the baselines aligned.
We've added a required constraint of top >= 40 to each element. That tells auto-layout "don't let either element be closer than 40-pts from the top."
We've added a constraint of top = 40 to each element, with Priority: 249. That tells auto-layout "try to put the top of each element at 40-pts... if you can't (because the baseline alignment is pulling it down), then go ahead and break that top constraint."
I have a UILabel (the subtitle) that I want to have a static X origin, but extend to the edge of its nearest neighbor. There's a button ("Visit Link") that is optionally removed from the superview at runtime if not needed. The constraint from the label to the button has a priority of 1000, and the constraint from the label to the superview container has a priority of 250:
However, when I run the application removing the button (via .removeFromSuperview() in the viewDidLoad method), via the view debugging I see that the content size is setting the width of the label, taking priority over the constraint I have set.
I expect the label to extend to the edge of the view, but as you can see, the constraint is greyed out - I assume trumped by the (content size) constraint instead:
Does the (content size) constraint have a higher priority than my Trailing Space to: Superview constraint? And how can I change it, since it's not a constraint I've even defined?
When you remove the button from the view hierarchy, that also removes any constraints involving the button. So, all that's left is the trailing constraint to the superview at priority 250.
The label has an intrinsic width based on its content. That means that its horizontal content hugging and compression resistance priorities come into play. Its content hugging priority is 251.
That means that it's more important to the auto layout system that the view's width be no larger than necessary than it is to keep its trailing edge at 8 points from the superview's trailing edge.
You should probably increase the priority of the trailing constraint. You want it to be less than the trailing constraint to the button so that, in the case where the button is present, it doesn't conflict. You also want it less than the button's compression resistance priority, so that the button doesn't get squished to allow the label to be 8 points from the superview. But, other than that, you want it to be as high as possible. (In the hypothetical case where you simply got rid of the possibility of there being a button, you would normally make that trailing constraint required, right? So, it should be as close as possible to required without causing undesirable side effects.)
If you're targeting deployment to iOS 9.0 or later, you should consider using a UIStackView for this layout. It will take care of some things for you, like adding or removing the appropriate constraints when the button is hidden or shown.
The (content size) constraint, automatically installed by the system at runtime, seems to have a priority somewhere between 250 and 750. When I use 250 or 251 for my Trailing Space constraint, it does not work.
However, bumping the priority of my Trailing Space constraint up to the Xcode-titled High priority of 750, allows it to take precedence. So the defaults for the width of UILabel seem to fall "somewhere in the middle."
Autolayout, you silly.
From the second screenshot, it looks like everything is working as it is supposed to.
Two things I'd like to mention:
Always try to set up your layout so you have as few constraints as possible. Each constraint adds complexity.
Yes, content size of the label does indeed have a higher priority. That's why the label is not resized to the right-most edge, which is good. What you are seeing is the label's intrinsic size which means that UIKit knows how big the label is supposed to be drawn, depending on font, text, etc.
To make this layout a bit more robust, I'd change the Trailing space of the label so that it has priority 1000 but is >= 0.
This way, the layout will be valid with or without the Visit Link button and the content size of the label (it's intrinsic size) will resize it to whatever length it needs to be, but no more than the right edge of its superview.
Hope this helps!
UPDATE: Wrote a quick post on why this content size is appearing and why you should use it to your advantage.
I am testing my app on an iPhone 4 and iOS Simulator (4S). The label positioning is fine on the simulator but displays incorrectly on the device.
I am using the following constraints on the label:
What could be causing this to happen?
You have a case of conflicting constraints. Let's step through the rules you applied to your label:
Align Center X to Superview
This is just aligning the center X of your superview with the center X of your label. No trouble here.
Bottom space to: SIGN UP <= 50
This constraint applies the rule "make the bottom of my label at MOST 50 points away from SIGNUP."
The issue is with your last constraint:
Top Space to: Top Layout Guide >= 5
This constraint adds the rule that the top space of your label must be AT LEAST 5 points away from the Top Layout Guide.
Auto Layout recognizes it can't possibly satisfy both the Bottom Space and Top Space constraints at the same time, so it destroys your bottom constraint, resulting in the bad behavior.
Your top constraint is still valid, because in that case the space between the top layout guide is greater than 5.
Try making your Top Layout Constraint a <=, or lowering the content compression resistance priority of your label.
The answer is in your constraint itself.
It shows that the topspace can be greater than or equal to 5. And bottom space can be less than or equal to 50. So that's why it shows such a behavior.
In my suggestion edit that constraints like:
Top Space equal to 5
Bottom Space less than or equal to 50
I'm unsure of how I should set up some constraints in my auto-layout storyboard in iOS.
I have a textbox and a button next to it, constrained to each other, and they're both constrained to the sides of their superview by 20. It works great for iPhones/iPods. Moving up to iPad, however, the textbox becomes too wide. Is there some way to set a "max width" on the text box in order to allow some growth, and allow the constrains to the superview grow after that point?
It feels like I'm approaching it all wrong, do I have the wrong approach?
The constraint
You can set a constraint for "width less than or equal to X" (whatever your value X is).
Then pair that with your "trailing edge space to the edge of the superview is equal 20".
The Conflict
This will not work as it is though as if the superview is too wide it will create a conflict.
Priorities
What you can do though is give the "edge space" constraint a priority of 1 less than the priority of the width constraint.
So if the width constraint has a priority of 750 (which is default) then give then space constraint a priority of 749.
This has the effect of telling AutoLayout. "If there is a conflict, then break gracefully by 'removing' the space constraint and keeping the width constraint".
You can set a width constraint and in the right side of the screen, instead of equal, select less than or equal (or something like this). This will add a maximum width to your UI element.
You can add width constraint which can be set the constant with the equal value, for example 100 or you can add equal greater than 100 or less equal...
you can use Ctrl+Drag with in a text field to set fix width.
I can not understand why following setup gives me the Inequality constraint ambiguity error. The general idea (although simplified in this question) is to pin the View to the top and left, give it fixed height and variable width. Thus, 4 constraints have been added: 3 with equal values (for top, left and height) and 1 with less than or equal. And it gives me that error. Finally, what I am trying to achieve, is to put other new to the right of this View, and specify the spacing between them. So, whenever Left view changes the width, the right View will change the X.
It is ambiguous because your width constraint is ambiguous: a less than or equal constraint is not constraining enough. What you probably want, if I can infer what you are doing correctly, is to set the width constraint to a fixed value that you want initially, and then create an IBOutlet for that constraint, as well as for the Right View's X constraint, and change those programmatically when needed. This way you can programmatically change the width of your Left View, and at the same time update the Right View's X position.