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).
Related
I am working on an iOS 11+ app and would like to create a view like in this picture:
The two labels are positioned to work as columns of different height depending on the label content. The content of both labels is variable due to custom text entered by the user. Thus I cannot be sure which of the the two labels is higher at runtime and there is also no limit to the height.
How can I position the BottomView to have a margin 20px to the higher of the two columns / labels?
Both labels should only use the min. height necessary to show all their text. Thus giving both labels an equal height is no solution.
I tried to use vertical spacing greater than 20px to both labels but this leads (of course) to an Inequality Constraint Ambiguity.
Is it possible to solve this simple task with Autolayout only or do I have to check / set the sizes and margins manually in code?
You can add labels to stackView
One way to do this is to assign equal height constraint to both label. By doing this height of label will always be equal to large label (Label with more content).
You can do this by selecting both labels and adding equal height constraint as mentioned in screen short.
Result would be some think like below
The answer given as https://stackoverflow.com/a/57571805/341994 does work, but it is not very educational. "Use a stack view." Yes, but what is a stack view? It is a view that makes constraints for you. It is not magic. What the stack view does, you can do. An ordinary view surrounding the two labels and sizing itself to the longer of them would do fine. The stack view, as a stack view, is not doing anything special here. You can build the same thing just using constraints yourself.
(Actually, the surrounding view is not really needed either, but it probably makes things a bit more encapsulated, so let's go with that.)
Here's a version of your project running on my machine:
And with the other label made longer:
So how is that done? It's just ordinary constraints. Here's the storyboard:
Both labels have a greater-than-or-equal constraint (which happens to have a constant of 20) from their bottom to the bottom of the superview. All their other constraints are obvious and I won't describe them here.
Okay, but that is not quite enough. There remains an ambiguity, and Xcode will tell you so. Inequalities need to be resolved somehow. We need something on the far side of the inequality to aim at. The solution is one more constraint: the superview itself has a height constraint of 1 with a priority of 749. That is a low enough priority to permit the compression resistance of the labels to operate.
So the labels get their full height, and the superview tries to collapse as short as possible to 1, but it is prevented by the two inequalities: its bottom must be more than 20 points below the bottom of both labels. And so we get the desired result: the bottom of the superview ends up exactly 20 points below the bottom of the longest label.
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'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.
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.