ios8 cell constraints break when adding disclosure indicator - ios

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.

Related

NSLayoutContraints: How to position a bottom view below the higher of two labels?

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.

Xcode 9 - "Fixed Width Constraints May Cause Clipping" and Other Localization Warnings

I downloaded the new Xcode and in Interface Builder I'm having a ton of problems with warnings that say things like:
Fixed Width Constraints May Cause Clipping
It looks like this:
I do have localization for several languages and I understand the warning that in another language a label's size may change, but my app doesn't have this problem. I ran and tested it in Xcode 8 yesterday, it was fine. I don't want to spend hours and hours adding pointless new constraints.
Any suggested solutions?
I was getting the same warnings even without multiple languages in my app, which led me to find out what was really going on. . .
There are a few different things going on here. I was able to silence the fixed-width warnings in my own app by changing the width of the object spacings from fixed width to greater than or equal or less than or equal.
This can be done by selecting the object in interface builder, going to the size inspector and changing it there:
Or, select the constraint from the document outline, go to size inspector, and change it there:
As far as the warning at the top of your screenshot:
Fixed leading and trailing constraints with a center constraint may
cause clipping
Here is a screenshot from my own app in which I was getting the exact same warning:
I had the label with the # sign set to leading and trailing to the buttons but also to align the center with the rating label. Once I removed the center alignment constraint, the warning disappeared, but I was left with an improperly laid out set of objects.
It is then that I resigned myself to embrace the Stack View. As annoying as it is to use, when you get all of the constraints and settings right, it lays out beautifully and with no warnings.
Edit
As Repose writes in the comments, sometimes simply adding >= 0 will be what you need, as you are making sure two elements do not overlap.
You can try Disabling "Respect Language Direction" on per Constraint basis to silence the warning and see if it helps. Select your constraint and open Attributes/Size Inspector. Please see image attached.
If you are not planning on localizing your app to other languages, then this solution should not have any drawbacks. For localized apps you have to be more conscious of your label and font sizes.
p.s. This solution works for iOS. For macOS try >= or <= to silence the warning.
p.p.s. Labels in the picture below are much easier to create using AutoLayout and attributedString property on a single UILabel or UITextView using NSMutableAttributedString. The image is for demonstration purposes only.
For labels and buttons which are localized this warning makes sense and you should provide the necessary constraints so your labels don't overlap. If they don't overlap now they might in the future, so it won't hurt to provide the constraints.
Xcode helps you add these constraints automatically:
In the document outline of your storyboard click on the yellow arrow and either choose "fixed leading" or "fixed trailing", depending on where the text is on your screen (left or right). This will fix it for most issues.
If you have this issue with a Button without any text (only image), try to remove the "default title" which might still be set for the button:
With Labels, you can set Lines is 0 and Autoshrink properties is Minimum Font Size to remove Fixed Width Constraints May Cause Clipping warnings, like this:
Another quick solution !
For a UIButton by changing the title from plain to Attributed text also resolved my issue:-
I know this question has already been answered but what I did to fix this error in my case was to add the "Aspect ratio" property and then eliminate the width or height constraint this worked pretty well and was less effort, and I managed to keep the same output and adapt my view for the different devices.
Swift 4 , Xcode 9.1 :
About this issue, I think your object don't know what it's the correct center position in the context of it's superview, and using remove, greater than or other leading/trealing settings most of times don't work correctly. First, you must check the correct constraints of your superview.
If your superview/s are correctly setted, you can try to "explain" to your object what is the correct position in the view by setting the "horizontally in Container" constraint:
If you need fixed width constraint for button just set width constraint priority to 700.
I had the same problem, but when I changing to >= it automatically set the constant to 0, if I choose 60 for instance, the warning appears again. So I was in a loop with the problem.
I could fix embedding my Label in a View
Editor > Embed In > View
In Label I set Top, Bottom, Leading and Trailing with constant = 0
In View I set the constraints that I was expecting before.
I had the same problem when moving to Xcode 9 and found an approach that's useful for certain kinds of layouts. In my case, I wanted a table header in which two columns (UILabels) were of fixed width and another was of variable width. Regardless of how I specified column widths (including using constraints greater than or equal instead of equal, etc.), I kept getting the warning about possible clipping. In my case, I wanted the variable width column (UILabel) to clip if necessary. I could have just ignored the warning, but don't like doing that.
The approach that worked here was to create a UIView with appropriate size constraints and embed the UILabel as a subview in the UIView. Then truncation happens if necessary and I get no warning. This works whether the UIView/embedded UILabel is in a StackView or not.
This is essentially the same approach as that of Haroldo Gondim but here you can see it also works with or without StackView.
The following image shows the approach, with and without StackView. "SpacerName" is a variable width UIView containing a label and "SpacerPD" is one with a fixed width of 80. [Colors are not significant; just there to show where the views are.]
As you can see in the image below, I was having the error "Fixed Width Constraints May Cause Clipping" because although I had set my textbox to be vertically centered and my label to have a left margin constraint, I hadn't defined a constraint for the text box in relation to the label, so XCode was alerting me that the textbox could clip (be rendered above) the label.
After adding the left constraint to the text box to always stay some distance apart from the label the error was considered solved by XCode and it didn't bothered me with the constraint warning anymore.
I had a similar issue when trying have the button with the same paddings from the edges of the super view.
I've ended up using horizontal center constraint and equal widths constraint to the super view.
To Fix The Error: Fixed Width Constraints May Cause Clipping” and Other Localization
You need to select the view/object, go to the "Show Size Inspector", find the Width Constraint and set the Constant to Greater or Equal to:
To Fix The Error: Leading/Trailing constraint is missing which may cause overlapping with other views
This means that the view/object Xcode is complaining about, is missing a Leading or Trailing Constraint to a neighboring view.
While holding control, drag to a near by view/object
Add a Leading or Trailing Constraint

Strange behaviour in Storyboard (Missing view when running app)

For the last few months I have been doing all of my layouts in code to practice with autolayout.
I have decided to go back to Storyboards and am regretting it already :-). I have been trying to do a layout with multiple views including a UIStackView and was getting many errors. I thought maybe I was doing something wrong with the StackView so I decided to delete everything apart from a button and label.
The simple view with a label and button can be seen below:
As you can see, the constraints on the label are very simple. The button in the top right (where the blue is) just has top space and trailing space. However when I run the app the label is not seen:
I am also constantly getting: An internal error occurred. Editing functionality may be limited.
Has anyone else experienced this odd behaviour?
You have set the leading and trailing of the label . This will definitely happen because you have set the wrong constraints. Check the size of your view Controller where you have set the constraints, its (Any width , Any height).
Now, check in which size you are running, its (Compact width, Regular height). If you run it in a iPad you will find your label. Since, you have not fixed the width of your label, the label gets compressed when it is run. Try setting the width of your label and remove either the leading or the trailing constraint.
Else instead you can fix your width and select either the leading and trailing and set it to "less than equal to " rather than "equals to".
While designing imagine yourself holding the object keeping in mind about all possibilities that can occur and then set the constraints accordingly. A user can run your application in any size iPhone and iPad. So, the design should be such that it doesn't affect your objects in it.
If any problem occurs try view debugging then you can definitely find out where you have done wrong.
You may need to remove label's leading & trailing space, and add add constraint call "Center Horizontally in Container" if you want to center the label.
You can learn more about autolayout in here if you do not know it well.
You have hard coded leading and trailing constraint constants. They add up to 518. That probably makes your label width negative. You don't see it in Storyboard because you're using the inferred simulated metrics. You'll see it right away if you change to an 3.5 inch iPhone.
I think a better approach would be to remove the leading and trailing constraints and center the label horizontally in the superview. You can set a fixed width on your label if you want.

I don't understand constraints priority

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

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.

Resources