I am trying to convert my app to use Auto Layout.
This is how it is supposed to look (before I used Auto Layout):
Now I am unsure how to achieve the following using Auto Layout:
The left text label (with 22:35 in it) must be in the horizontal
center of the KL1032 label when the right label is not present (with
-14 min in it).
If the right label actually is present, then the right edge of the
22:35 label should align with the horizontal center of the KL1032
label and the left edge of the -14 min label should also align with
the horizontal center of the KL1032 label, leaving a little space
between the labels just as in the screenshot.
Which constraints do I need to use for achieving this? Do I also use Content Hugging priority's?
I tried just centering the 22:35 label, which is fine if the right label is empty/not present. But when the right label is present, this of course does not work correctly.
Embed each label in a view and give all the views a minimum width constraint of 14. Then the layout adjusts as you describe when the right label is empty. So, you layout the embedding views:
You're going to have to add and remove the "(-14 min)" label rather than just hide it or set it to be empty.
Set the 22:35 label to be have its horizontal center aligned with that of the KL1032 label, but with a somewhat reduced priority (say 750). Also set a constraint so that the trailing edge is greater than or equal to the center of the KL1032 label minus whatever slight spacing you want. This one should be at priority 1000 (required).
When the "(-14 min)" label should be present, add it and set up constraints on it. Constrain its leading edge to be the trailing edge of the 22:35 label plus the spacing you desire. Also create a constraint to align its center with the center of the KL1032 label, but set its priority to be between required (1000) and the constraint centering the 22:35 label, for example 800. The layout system won't be able to center it because that would force the 22:35 label past its required constraint, but it will get it as close as possible.
When that label should not be present, just remove it from the hierarchy, which will also remove its constraints. The 22:35 label will move back to being centered (because it can and it "prefers" to).
If you prefer, you can do the layout in the NIB with both labels present. Make outlets to the "(-14 min)" label and also the constraints on it. Make them strong because you'll be removing them from the hierarchy temporarily but don't want them released. That way, your code can just remove and re-add them as appropriate, without having to express the constraints in code.
Edit: Oh, and you'll want a constraint setting the baseline of the "(-14 min)" label to be equal to the baseline of the 22:35 label. You'll have to add that each time in code or set it up in the NIB with a strong outlet and re-add it each time, just like the others.
Edit 2: Another approach occurred to me. You could leave the "(-14 min)" view in the hierarchy and all of the constraints in place all the time. When you don't want it to show, set the view to hidden and set the constant of the constraint between its center and the center of the KL1032 label to be a much larger value. Definitely large enough to allow the 22:35 label to take its preferred position of being centered, potentially large enough to be well off-screen.
Since you don't want the 22:35 label to follow it all the way over, the constraint establishing the spacing between those two labels should be made to be "greater than or equal" rather than "equal". This change would not be conditional on whether the "(-14 min)" label is showing. It's just how that constraint should always be.
When you do want the "(-14 min)" label to show, reset the constraint that tries to center it back to having constant equal to 0. Also, of course, unhide it.
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 cell in which I place four buttons and four labels. Each button gets assigned a picture with width 50 and height 50. Furthermore, all buttons have a corresponding label describing what they're intended for.
My objective is to have the buttons and labels resize to keep the buttons' and labels' aspect ration intact while the screen dimension changes on different devices. I have been playing with auto layout changing the hugging and compression to achieve this but haven't been successful yet. Any help would be much appreciated...
I think you should take a look at a UIStackView, because this seems exactly as a use case for stack. Just put each pair button/label in a stack, and then all four pairs into a horizontal stack, which you constraint to the cell itself. You should be able to handle all you need just by configuring the stack’s properties (axis, distribution, alignment, spacing).
Embed your button and label into a view. Set the width of this view equal widths to content view and change the multiplier value to 1:4. This will adjust the widths of the views according to superview. Also, set the top and bottom constraint to 0 for this view.
Provide center align y-axis constraint to button after setting the width and height constraint to 50. Set its top constraint to a value you deem fit.
Set labels's leading and trailing constraint to a value like 8. Choose center alignment for text. Also, provide top constraint to buttona nd bottom to its superview.
Copy the view and paste to create the three views and provide them equal widths constraint to the first view. Also, provide their leading, trailing, top and bottom constraints.
Here are a fast tutorial in how to achieve that:
1-
2- completion of the first Gif:
Note you can achieve the same output using a UIStackView
I've been looking at many differnent solutions using size classes and Autolayout and still haven't been able to get a consistent UI on different sized screens. This seems like it would be a pretty trivial thing to do but I'm on my second day struggling with this. I'm developing on the iPhone 7 in my story board, but when I switch preview to other devices the layouts get messed up. Here are two screen shots demonstrating the issue: iPhone7View, iPhone4sView.
What I've tried:
For the App label, I set the leading, trailing, verticle spacing to top layout guide, and verticle spacing from the App label to the email text field.
For the email field I set the leading, trailing, and verticle space to the password field.
For the password field I set the leading, trailing and verticle space to the sign in button.
For the sign in button I set the leading, trailing and verticle spacing to bottom layout guide.
Additionally, I also set the constraint so the email and password fields have the same width and height.
Is it possible to achieve this using only the storyboard, and make the views adapt to the screen size?
I would lower the priority of the fixed constant constraints, and then create another set of more flexible constraints for the minimum spacing that you want.
As an example of what I mean, for the App Name label you would keep the original top space constraint that you have, but lower the priority to, say, 750. You would then create another constraint for the top spacing and make it something like >= 40 (whatever you want the minimum spacing to be), and the priority for this should be higher than that original constraint (i.e. keep it at 1000).
Yes it is possible. Here's what I would do...
For the App label:
anchor to the parent's top layout guide.
center to parent horizontally.
let the text determine the rest.
For the sign in/sign up button:
anchor to parent's bottom layout guide.
anchor to parent's leading and trailing edge.
let the text determine the height.
For password text field:
vertical space to sign in/sign up button
anchor to parent's leading and trailing edge.
fix height.
For the email text field.
vertical space to password field
anchor to parent's leading and trailing edge.
fix height.
In response to your comment, you can't use percents with constraints in storyboard. If you want to use percents, you will have to adjust the constraints in code.
Another option would be to use a stack view. Here is an example:
https://www.dropbox.com/s/d7yfgjvafweqykq/example.storyboard?dl=0
I'm desperately trying to constrain the first baselines of a UILabel and a UITextView in Interface Builder. From my understanding this should be quite easy by simply adding the following constraint (Pseudo-Code):
label.firstBaseline = textView.firstBaseline
However, depending on the other constraints and some view settings I get really awkward results as shown in the following screenshots.
1st Case: Disable scrolling & Align the top edges
This is only for showing the general view setup. I have disabled scrolling for the textView to avoid ambiguity. (Otherwise the textView cannot calculate its own height from its containing text.)
2nd Case: Disable scrolling & align the first baselines
Same setup as in the 1st case with the only difference that I have removed the constraint that aligns the top edges of the views and added a constraint that aligns the first baselines of the views instead.
For some reason, the label "takes off" well beyond the bounds of its superview and ends up at a y position of 7764. No idea why.
3rd Case: Constrain height & align the first baselines
Now I have scrolling enabled for the textView and constrained its height instead.
Obviously the label's first baseline gets aligned with the textViews top edge even though the constrained says to align both their first baselines. I read in the Apple docs that the firstBaseline parameter returns a view's top edge if that view doesn't have a baseline. So it seems that the textView doesn't have a first baseline in this scenario.
Does a UITextView in general have no firstBaseline?
And if so: Why? After all, it's a view that draws text and thus this property should be set.
Any thoughts on this are appreciated.
I'm currently setting up a login screen that should ultimately look something like this:
I've successfully accomplished all the constraints needed to create the desired look on all screen sizes, however, I can not seem to figure out the constraint needed to correctly align the "Don't have an account? Signup". I've designed it so the "Don't have an account?" is a UILabel, and "Signup" is a UIButton. I've just aligned these side by side.
The UILabel, "Don't have an account?", has the following constraints:
The UIButton, "Signup, has the following constraints:
These constraints seem to accomplish what they need to on screen sizes 4" and below. however the 4.7 inch, and 5.5 inch (iPads excluded), have wide spacing between the UILabel and UIButton. I've read Ray Wenderlich's tutorial on auto layout, and still can not figure out the problem (I'm new to auto layout, but I have an idea what I'm doing. I'm not just adding random constraints hoping it works).
This is what it looks like on all my targeted devices:
Notice as screen size increases, so does the gap in between the label and button. Any help is appreciated.
You are setting the leading space of the label to its superview, and you are setting the trailing space of the button to its superview. So obviously if the superview gets wider, they are going to get further apart.
So if that's not what you want, don't do that.
The actual solution to what you want to do is quite tricky. You want these two objects to behave as a group, and you want that group to be centered. There's no simple way to express that. You will need to make a group. In other words, you will need a container view, give that view an absolute width and center it, and put these two objects inside the container.
New in iOS 9, a UIStackView can help you with this.
Another approach that works is to use a couple UIViews as spacers. I sometimes prefer this to containment, but it is probably a matter of taste.
Remove the leading constraint from your UILabel.
Remove the trailing constraint from your UIButton.
Add a UIView with clear background in front of your UILabel. Set a leading constraint from this UIView to the container leading edge with constant 0. Set a trailing constraint to the UILabel with constant 0.
Similarly, add another UIView after your UIButton. Set a leading constraint from this UIView to the UIButton with constant 0. Set a trailing constraint from this UIView to the container trailing edge with constant 0.
Both UIViews will need a Y position and height. These are somewhat arbitrary so I would set Align Center-Y constraints with your UILabel, and height constraints of say 5.
Here's where the magic happens: select both UIViews and set an Equal Widths constraint. This forces the UIViews to occupy equal space on both sides.
UIStackView is overkill for this situation. Just
Put the label and button in a single UIView.
Constrain the label's left and center Y to the container view.
Constrain the button's right and center Y to the container view.
Constrain the label's right edge to the button's left edge.
Set the horizontal content hugging priority of the containing view to 1000.
Constrain the containing view's center X to its superview.
You can do it to your existing code only by setting few constraints. And this would work on all resolutions even on the iPad. You don't need to group them , i have attached the images to show the constraints that i have applied.
And for the button set this constraints
It would be shown properly on all the devices kindly have look as to how it looks on all the resolutions and also in the landscape mode of each resolution
Thanks
Omkar