Why does Autolayout not produce consistent results? - ios

Either I'm out of my mind, or AutoLayout is straight broken. Can someone please explain this to me. I have a TableViewCell in a TableView that spans the width of the ViewController. I put 4 Labels inside my TableViewCell. I created constraints using AutoLayout such that each label is 25% the width of the TableViewCell. And yet, the 4 labels are CLEARLY different widths and they don't even add up to 100% of the width of the entire cell. Here's the screenshot. (Horizontal position of each of the labels is ambiguous, yes, but that shouldn't make a difference). Why are they not the same width? And why does 25% + 25% + 25% + 25% not add up to 100%? Running XCode 7.2 and targeting iOS 9.

This red error symbol is Interface Builder telling you that it cannot solve your constraints. In this case, as you have said, it's likely because you have not provided x position constraints for the labels.
Auto layout can either solve all constraints and get a right layout, or it can't and the result will be undefined. Remember that auto layout is an algebra-based process that solves for unknown values by using known values that you provide in constraints. If you don't provide sufficient and unambiguous known values, the equations for the remaining values simply cannot be solved and there can be no expectation of a correct result. The solution is to create enough constraints to make the layout solvable.
As a note, as of iOS 9 I would suggest using a UIStackView to hold those labels. UIStackView exists precisely to take the pain out of setting up manual constraints for these types of scenarios. If you used a horizontal stack view in the cell, you would constrain its edges to the cell's edges, drag the 4 labels into it and set it to "Fill Equally". And that's all you would need!

Daniel Hall's answer has useful information, but doesn't tell you specifically why you're seeing what you're seeing, so I will.
Xcode doesn't always enforce your constraints in the storyboard editor until you ask it to. In this case, you can select the table view cell's content view and from the menu bar choose Editor > Resolve Auto Layout Issues > All Views in BBRowTableViewCell > Update Frames. (Sometimes it takes two or three tries for Xcode to get everything right.)
However, you probably won't like the result. Because you haven't constrained the horizontal positions of the labels, Xcode will probably pile them all up at the left edge of the cell, or maybe somewhere outside the bounds of the cell where you can't even see them.
If your deployment target is iOS 9 or later, the easiest solution (as Daniel Hall said) is to put the labels in a UIStackView set to “Fill Equally”, and constrain the stack view's edges to the cell content view's edges.
If your deployment target is earlier than iOS 9, then you should create the constraints described by user3802077.

This is not the only way, but here is how I usually do it.
As you did for the top and bottom for each labels, then:
Leading of label1 to leading of superview
Trailing of label4 to trailing of superview
Then a constraint for each neighbouring label:
label1.trailing to label2.leading,
...
Then put an equal width constraint from each labels to label1:
label2 to label1
label3 to label1
label4 to label1
This should be it. No need of specifying 25%.

For autolayout constraints it requires to give for constraint to particular object
x,y position and hight , width
If you are not giving any one of this it shows error to you.
So make sure to give all the require constraints to your object.
Other option is uistackview for the ios 9 and later.
Here is a link for you to learn about stackview
https://m.youtube.com/watch?v=XqVWyA5PLwk

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.

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.

How to setup autolayout constraints properly for self-sizing cells

I've seen a lot of guides about self-sizing cells in iOS 7 and 8. Unfortunately, all of them shows very simple cases (I mean two UILabel's with same width and which placed like the first under the second and their constraints are very simple too). So I've a situation that seems to be not so much difficult but I can't resolve a problem.
I believe that it is very easy for the most of people and hope that somebody can help. The problem is that there is UITableViewCell with 3 child views inside.
Every view is UILabel. Labels placed in such order: two labels at the top of UITableViewCell with fixed size and one at the bottom with fixed width but with dynamic height. How should I setup my constraints properly to make my UITableViewCell be self-sizing?
The key to self-sizing is a vertical set of constraints that will determine the cell height. This isn't that complex, as you don't have two horizontal labels that vary in height.
Since headerLabel and dateLabel height don't vary, you only need to constrain the dynamicLabel to one of the two top labels.
In this example, we'll arbitrarily pick headerLabel to use in the vertical constraints. The spacing between the labels wasn't specified, so I'll assume it's 10. Adjust as necessary.
For your horizontal constraints, set leading and trailing space to the superview.
Your vertical constraints would simply look like "V:|-10-[headerLabel]-10-[dynamicLabel]-10-|" (and should
dynamicLabel will grow as tall as it needs, provided you set its numberOfLines to 0, and its height will end up determining the height of the cell.
Assuming the constraints are setup properly, and the storyboard cell height exactly matches the vertical height set by the constraints, you shouldn't see any storyboard warnings or errors, and should be good to go.
8.4 does address a number of issues, so you shouldn't require any specific (layoutSubViews/preferredMaxLayoutWidth/reloadData) code to work around earlier 8.x problems.
As an aside, a general tip is to pin your constraints to the superview margin, instead of the superView. This means your leading, trailing, top, and bottom constraints could be 2, instead of 10, since the margin is generally 8. This lets your white space adapt to different devices, which is a really nice touch.

UIScrollView with multiple multi-line labels and AutoLayout?

Is there a way to achieve this? I have tried literally everything and nothing has worked for me yet.
So basically what i want to do is the following: I have a scroll view with some labels in it. All the labels get their text from a server and I have set their number of lines to 0 so that they change their height according to the amount of text. However, this does not affect the scrollview content size(even though my labels have constraints set up to the bottom,top,leading and trailing of the scrollview) and the labels go off screen and I am unable to scroll down. Can someone point me in the right direction to how I would set up my constraints, my view hierarchy and etc?
Any help is much appreciated! :)
Late, but this solved it for me:
Set leading (I have a 32pt inset), trailing and top constraints. The trailing will not actually seemingly do anything..
Make the trailing Greater Than or Equal to avoid localization alert.
Finally, add a new Equal Width constraint to the label matching the scrollview. Use the constant to subtract the required padding (I used 64 due to mirror my leading inset).
And voilà! The Label will align correctly both in IB and in-app.
In Scrollview the last view's bottom constraint is so important. You should set its priority to 250 and put it to Greater than or equal.
Remember you should only change the bottom constraint of the last view, which in my case it's the continue button.
I would consider using UITableView instead, it has several benefits:
It allows for reuse of cells, if all the cells look the same
It manages recycling of cells when the number of values you're getting from the server increases (decreases memory pressure when number of cells becomes substantial)
It allows for more flexibility with the content (it's quite often for design to change last second or to evolve over the course of the project)
Most importantly, UITableView support auto sizing cells (as of iOS8), you need to specify the constraints between the label and the borders of the cell
There are several resources to start with:
http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout
https://www.captechconsulting.com/blogs/ios-8-tutorial-series-auto-sizing-table-cells
http://www.appcoda.com/self-sizing-cells/
Use a container view in a scrollView
Add constraints to superview (leading, trailing, top, bottom, height,width)
Make IBOutlet of constraints that you are going to update.
Add you all labels inside that view.
Update constrains/frame of your label so that it fits the text.
How much you increase the label height you should increase the container height too.
If the label count is not fixed use custom label class to add subview.
Perhaps you should need to understand how ScrollView works in Storyboard with autolayout.

Complex AutoLayout for Cell with dynamic size

I have a cell that contains a container with 10 subviews (two of them are simply bounds and the others are labels). The scheme looks like this.
Dynamic labels may contain huge text so the cells should conform the appropriate size to fit the content. The question is how to set up all the constraints manually... I've tried a dozen of times to do it myself but seems I'm not that good at this. The table view supports auto dimension for row height and uses custom estimated height.
In Storyboard it looks this way.
Where blue views are a subviews of View C. A grey view behind is a View B. Bold labels are static and the others are dynamic.
Demo project.
How to setup constraints?
Thank you very much in advance!
I managed to setup your constraints so that you get the result you needed. This is what I get:
I hope this is how you wanted it to look like.
Here is a link with the project.
I will try to explain how I added the constraints so that it makes more sense.
First of all, you have view B which needs to be as big as the contentView. For this you add top/bottom/left/right constraints to the superView. Because you are using automatic dimensions, if you add all constraints with priority 1000(the maximum one), you will get some error with the constraints while running. This is because, before the cell size can be calculated automatically it is zero, so the constraints crash. Therefore, I set the priority for top and trailing space with a priority of 999 so that you don't see the error log anymore. The result is the same.
Then views C needs (top or bottom)/left/right and height constraint
Then you need to add the constraints for the labels. Since you need the right ones to have multiple lines, the constraints need to specify the vertical layout for this particular case. So, you have as follows: first label: top/left to name label and right to super view. All the other have top to the previous one,and bottom to the next one.
for the labels that don't need to resize you just need leading space to parent,horizontal space to the right label and static width. Also, you will need a constraint to align the top with the label on the right.
This is the result I get:
Hope my explanation made sense, just let me know if you have questions. Good luck with your project!

Resources