I don't understand constraints priority - ios

I create a rect with following constraints:
I though if i set priority to constraints with width >=250 to 999, then width equal == 200 will work on small devices, and with width >= 250 on large.
But it is not worked. I read documentation:
After solving for the required constraints, Auto Layout tries to
solve all the optional constraints in priority order from highest to
lowest. If it cannot solve for an optional constraint, it tries to
come as close as possible to the desired result, and then moves on to
the next constraint.
This combination of inequalities, equalities, and priorities gives you
a great amount of flexibility and power. By combining multiple
constraints, you can define layouts that dynamically adapt as the size
and location of the elements in your user interface change.

The priority for the constrains will be applied in order to resolve a conflict between two different constraints. The frame's view will be modified applying the constraint with more priority. So, you should have another view or use the superview to apply the priority to the constraints.
This is a nice answer explaining the resistance priority:
Cocoa Autolayout: content hugging vs content compression resistance priority

iOS does what you tell. Constraints do not disable automatically depending on screen sizes. Constraints with higher priority will be applied first.
So, when iOS needs to layout your view, it will lookup the specifications that you provided. Now, according to your given specification, the view should have the width of 200 (highest priority) irrespective of screen sizes. So this constraint is applied and you get your view with width of 200.
How can you do what you want?
You should try to disable the constraint with "width" specification of 200, when your app is running on large screen then your other constraint will work and give correct result.
For disabling/enabling constraint -
Stackoverflow Link
Apple Developer Library

Related

iOS autolayout disregarding "trailing space" constraint, using width/content size instead?

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.

iOS auto-layout unsatisfied constraints due to 0 sized container view

I put a view inside a container view with constraints:
#"H:|-15-[myView]-15-|"
And when the container view's created, its size is zero, and the code crashes, since it cannot satisfy the constraint that both left/right edges are 15 points apart from the left/right edges of zero sized container.
Is there a way to have some priority of this constraints, or is there a better way to do this? Assume the container cannot have a frame when created with the subview.
Thanks!
Yes, you can set priorities to constraints.
You have to add "#value" after the constraint constant like so:
#"H:|-15#750-[myView]-15-|"
Priority level. Constraints have a priority level. Constraints with higher priority levels are satisfied before constraints with lower priority levels. The default priority level is required (NSLayoutPriorityRequired), which means that the constraint must be satisfied exactly. The layout system gets as close as it can to satisfying an optional constraint, even if it cannot completely achieve it.
Priority levels allow you to express useful conditional behavior. For example, they are used to express that some controls should always be sized to fit their contents, unless something more important should take precedence. For more information about priority levels, see NSLayoutPriority.

ios8 cell constraints break when adding disclosure indicator

I have a problem with auto layout on IOS8, the simplest case I can recreate is a simple tableView. I setup a static cell and then simply add a label.
My aim is to have the label largely fill the space, so I have three constraints on the label...
Centre it vertically within the superview (I think this is fine)
Set the label trailing margin to 30 (relative to superview)
Set the label leading margin to 30 (relative to superview)
It's all absolutely fine and works perfectly with no major problems or warning (it does warn about zero height, but I don't think that so much of an issue for this)
Now ... if I add a disclosure indicator it all falls apart. It still looks ok, but I get the following:
2014-10-30 15:51:46.358 ContraintIssue[25572:1586028] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand,
refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7fd3f3d23390 UITableViewCellContentView:0x7fd3f3d226f0.trailingMargin == UILabel:0x7fd3f3d227e0'Label'.trailing + 30>",
"<NSLayoutConstraint:0x7fd3f3d235f0 UILabel:0x7fd3f3d227e0'Label'.leading == UITableViewCellContentView:0x7fd3f3d226f0.leadingMargin + 30>",
"<NSLayoutConstraint:0x7fd3f53b73b0 'fittingSizeHTarget' H:[UITableViewCellContentView:0x7fd3f3d226f0(38)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fd3f3d23390 UITableViewCellContentView:0x7fd3f3d226f0.trailingMargin == UILabel:0x7fd3f3d227e0'Label'.trailing + 30>
I don't understand why adding an indicator would cause such a problem, it's nothing to do with the scale of the numbers, I've experimented quite a bit.
Any ideas?
The real world example a cell that has a label (the label) and then either another label or a text view that contains a value that can be set by following the disclosure. So the first label is a fixed size, the second ideally needs to be the max that it can be, but truncate the text if needed.
(See the 'ringtone' or 'vibration setting' within adding a contact for an example of what I'm trying to achieve)
Many thanks,
Lee.
I just had the same problem. I want to layout an image-view on the left hand with a label on its right which fills the space between the image-view and the right (or trailing) border of superview (which is the cell's content-view). Accessory view is set to disclosure indicator as well.
As in your case the conflicting constraints where all H-based and one I found in the logs where fittingSizeHTarget. I didn't found out what this means nor where this was coming from, but I found your post here.
The following did the trick for me:
Lower the priority of your label's trailing-to-superview constraint. (I chose 990).
I assume, that the layout system (with the disclosure indicator visible) for what ever reason can't satisfy all the constraints anymore, so it breaks one. But if you lower the priority, it still tries to satisfy the constraint, but doesn't break it as the conflicting constraint(s) have higher priority.
Hope this solves your problem as well.
Please pay attention to what Stephen says in the comment section of the upvoted answer. The upvoted answer is somewhat correct, but it's important to understand why it should only be used in certain scenarios.
Priorities are usually used in context where element A has a constraint saying height is equal or less/more than 300, and element B has one says height is equal or less/more than 500. Then autolayout can satisfy both conditions based on their priorities.
In this particular example, both constraints are set to specific value and lowering priority essentially tells to ignore that constraint if it's impossible to satisfy (there's no "partially ignore it"). However UILabel has an exception - default UILabel behaviour is to resize itself to fit the content unless it's constrained by additional margins (autosize constraint hides under fittingSizeHTarget name) and this behaviour sometimes shows false warnings. In reality this constraint will be ignored in the runtime, but before it becomes disabled internally it will file a warning. Therefore, even though we tell to ignore one of our constraints by lowering its priority (the one we set priority to 900), since autosize constraint will be ignored in runtime, our 900 priority will be applied and satisfied.
Raimunda's answer above explaining about the intrinsic size that happens on labels, buttons, etc being where this fittingSizeHTarget log is coming from was spot on. While you can leave it and let the system deal with it, it's a risky hack, because you're relying on the system to break an unwanted constraint...which it may not do in future releases. And in some cases, as in a view example I recently dealt with, lowering the priority of the trailing constraint to the outside safe area didn't prevent the label from overrunning (it did, however, fix the log warning lol).
For content issues like this, use the Content Hugging Priority and Content Compression Resistance Priority values. If you know that your label, for example, will want to increase in vertical height but constrain to a horizontal value, then make the vertical content hugging priority (Content hugging = the view resisting being made bigger) lower than the horizontal.
The Compression resistance is the opposite of this.
Then set your outside trailing constraint to a greater than or equal and you're good to go.
In the stack view below, the label titled Fake 4 actually overran and wanted to become two lines. That caused a conflict involving fittingSizeHTarget (the intrinsic content size wanted to stay on one line and overrun the view width). The key here was lowering the Horizontal Compression Resistance priority to lower than ALL of the other content priorities. This allowed me to lower the priority of the >= constraint on the trailing value, and everything behaves as expected.
Anyway, tweaking this stuff is annoying as heck, but those Hugging/Compression values, combined with some form of >= / <= are usually the answer.
Hope this helps.

Inequality Constraint Ambiguity

i've a problem in resizing a UIView with Autolayout and constraints.
I'd like to change the origin (less than or equal of original) and the width (greater than or equal of original) but I got this: Inequality Constraint Ambiguity
Do you have idea for solve this?
thanks
I tried to make more than 1 vertical spacing constraint shrink for 3.5" displays, so I had to make 2 constraints between components that I wanted to shrink on smaller screen. One constraint was inequality (greater or equal) where I specified minimum required size, with 1000 priority, other constraint was equality constraint with specific size that is suitable for 4" screen, but with lower priority of 250.
This way Xcode stopped complaining and layout repositioned properly on smaller screen.
Your view is horizontally ambiguous. You do not have enough horizontal constraint information for the system to come up with just one solution for your view heirarchy. In this instance, it can't determine what the view size or left margin needs to be based on the current constraint information.
You need to add a less-than-required-priority (<1000) constraint either to your view's width giving it a defined width or add an equality constraint to your left margin constraint. By making the new constraint a <1000 priority, it will enable the new constraint to properly mix with your existing inequality constraints (which are required constraints). Here is another question that is similar to yours relating to inequalities.
The view will size differently depending on if you add the new constraint to the view's width or the view's left margin. This all depends on how you want your layout to behave in response to changes.
This does not make sense to the compiler (and logically) because there is no way to know whether the program should change x or width. Making one of the two static will solve your problem.
First you have to know what you want to do with constraints, please remove greater-than-equal-to constraint that does not make scene with less-than-equal-to constraint. buz view need define width constraint. either apply priority to which constraint play role first (greater-than-equal-to constraint or less-than-equal-to constraint).

Using autolayout on iOS how can I specify that a view should take up as much space as possible?

I am using autolayout in iOS to try and build a layout with fluid widths. The visual format for the constraint I am currently using is:
[self.scrollViewContainer addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-(>=32)-[viewToAdd(<=576)]-(>=32)-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(viewToAdd)
]];
That is to say: I want a minimum of 32px spacing on either side, and I want the viewToAdd to have a maximum width of 576px. This works well except that I want the viewToAdd to use up any available space while still meeting all the constraints. Currently I get the viewToAdd only ever being as wide as its intrinsic content size, and the spacing growing as needed.
Is there a way to specify that the viewToAdd should be as large as possible?
You're going to have to specify additional constraints on the view in order to get it to size the view to fill the remaining available space. Currently, you are setting the view minimums and maximums, but not setting any concrete constraints to give autolayout a more complete solution. In addition to the upper bound for the width, you need to give it 1. a starting point to solve the width for the view by either explicitly giving it a width at a lower priority, or 2. give the spacing on either side some more rigid constraints.
Based on what you've described, you need to constrain your view to give it some more substantial bindings to the superview on either side in order to let the system solve for the width. Since you would like to have the view size itself based on it's container's size, you will need to modify the spacing constraints (option 2 from above). In the above instance, you have specified only a minimum spacing, which would result in any of the following constraint solutions being found as valid by the autolayout engine for a 400pt superview:
|-32pt-[20pt]-------348pt-| <-- autolayout will probably choose this one
|-100pt----[20pt]---280pt-|
|-50pt--[20pt-]-----330pt-|
Which is probably not what you are wanting. Even more still, the width of the view can be anything between 0-576pt, which is also probably not what you're wanting. Since autolayout doesn't know what you want, it's simply using the intrinsicContentSize of the view for concrete sizing constraints. Since you chose 32pt as the spacing, a first step would be to give the spacing constraints some more substantial instructions, namely, telling the system that the spacing should be 32pts between the edges of the view and the superview unless the width of the view is >576pts. You would do this like so in your VFL string:
"H:|-(>=32,==32#900)-[viewToAdd(<=576)]-(>=32,==32#900)-|"
This says: "viewToAdd should have a maximum width of 576pts and have padding between itself and it's superview of 32pts. If the size of the superview grows beyond the maximum width of viewToAdd plus the initial padding of 64pts, the padding on either side should grow in order to continue to solve the constraint set."
This results in the following constraints being correct for a 400pt superview:
|-32pt--[336pt]--32pt-|
If you would like for viewToAdd to remain centered in it's superview when the view grows beyond the maximum, you will have to pass in the option NSLayoutFormatAlignAllCenterX to the options parameter in [NSLayoutConstraint -constraintsWithVisualFormat:]. If you did not have the >=32 constraint set on the padding, or did not set a priority lower than 1000 ("required" constraint priority level) on your padding's ==32 constraint, then your superview would be unable to grow beyond 640pts.
Not sure if this exactly answers the question, but for the ones using Storyboard: I found that you can add a width-constraint and constraints for both the distance to the left and right side of the parent view for example. If you then change the width-constraint to '<=' instead of '=' and set the priority of the distance constraints (both leading and trailing) to 750, the view will max out to the max width, respecting the distance-constraints if the view is too small.

Resources