Let's say you have a label on the right and another label on the left of a screen. Text on the left one can grow till the end of the screen. Is it possible to make constraints to push the left label down if the right one grows so big it starts to compress the left one?
Ps. without constraints it seems easy, but the question is about constraints
Is it possible to make constraints to push the left label down if the right one grows so big it starts to compress the left one?
Yes, but not by magic. You would need, in code, to detect that this has happened and actually change the constraints entirely in order to rearrange the labels.
A more common solution to this problem is to give the left label a fixed minimum size so that the right one cannot compress it beyond a certain amount. You can even let the right label grow to multiple lines.
I am attempting to position two buttons in a view so that they are constrained to the middle guideline - that is, I want the space on the leading edge of Pause and the trailing edge of Skip to vary the same amount so the two buttons remain centered in the view.
I've been unable to find a way to specify the middle guideline for alignment or constraint. Sure I'm just missing something :-) The auto constraints don't do the right thing at all.
Thank you!
Align them to center X and then set the constraint's constant to some positive or negative offset.
I have 2 labels that are stacked and have a bottom space constraint to their common container. I want to hide the lower label if there's no value for it and move the upper label down to the lower label's position. I can do that easily with an outlet bound to the bottom space constraint for the upper label.
The problem here is that these labels have different font sizes. So, when I set the same value for the bottom space constraint for the upper label, like I have set for the lower one, the upper label does not end up at the same vertical position.
I assume this is because these labels have different descenders, due to different fontsizes. It would be easy to solve that if I could set the bottom space constraint not for the frame of the text, but its base line. I'd like to do this in IB, if possible.
I don't think it is possible to set a constraint to the strings baseline. Your comma is way bigger in your upper label, thats why the numbers don't sit at the bottom of the label. If you make the label smaller, you cut those commas of.
I see two solutions here:
Leave it that way.
Set the constant of your constraint to something lower than 0 (I guess -2 to -5) so that your numbers perfectly align with the smaller label.
I want to have 6 objects (buttons) laid out inside one view. They should, however, follow some constraints:
Two top buttons should have the same vertical distance from superview (A)
Two bottom - the same (C)
Two in the middle should have their centers at the superview's center line
The vertical distances between all buttons (E) should be the same
and last but not least - the buttons should be square (so the width and height should be the same)
A = C
B = D
Is it possible to have this effect just in the IB, or should I use some additional code for the constraints?
This is a logical request, but constraints are defined using the attributes of views, but cannot not be defined in relation to other constraints. That having been said, there are a number of approaches:
Layout guides: An approach which doesn't require predetermining the any spacing is to have UILayoutGuide objects or, if using iOS versions before 9, just use hidden views, i.e. views with clear background or alpha of zero, in between the buttons.
The idea is to add these layout guides with addLayoutGuide (or add invisible views with addSubview if supporting iOS versions predating iOS 9) in between your six buttons as "spacers", and define the spacers to be the same size as each other, and with constraints between the spacers, the superview, and the buttons that will go in between the spacer. Once you lay that out (showing the horizontal spacer views in blue, vertical ones in red, just so you can see them):
The equivalent VFL for the constraints for those red UIView objects, called vspacerX, would be:
H:|[vspacer1][button1(100)][vspacer2(==vspacer1)][button2(==button1)][vspacer3(==vspacer1)]|
H:|[vspacer1][button3(==button1)][vspacer2][button4(==button1)][vspacer3]|
H:|[vspacer1][button5(==button1)][vspacer2][button6(==button1)][vspacer3]|
And constraints on the blue UIView objects, called hspacerX, like:
V:|[hspacer1][button1(100)][hspacer2(==hspacer1)][button3(==button1)][hspacer3(==hspacer1)][button5(==button1)][hspacer4(==hspacer1)]|
V:|[hspacer1][button2(==button1)][hspacer2][button4(==button1)][hspacer3][button6(==button1)][hspacer4]|
You don't have to use VFL to define these constraints, as any way you define these constraints will work, but it's just a concise format for describing the collection of constraints that I employed.
Anyway, when the view is rendered with those layout guides (or invisible views), it yields evenly spaced buttons like so:
Another approach is to have six "container" views, that would look like:
The equivalent VFL for these six container UIView objects might look like:
H:|[container1][container2(==container1)]|
H:|[container3(==container1)][container4(==container1)]|
H:|[container5(==container1)][container6(==container1)]|
V:|[container1][container3(==container1)][container5(==container1)]|
V:|[container2(==container1)][container4(==container1)][container6(==container1)]|
You can then add your buttons to that, centering one on each of the six little containers and then make your containers clear:
This works, too, but just a slightly different spacing (where the margins are half of the spacing between the views, whereas the other approach keeps the margins the same as the spacing between them.
Stack view: In a permutation of the prior point, in iOS 9, you can also use UIStackView, designed precisely for evenly spacing views. In this case, put two buttons each in three horizontal stack views, and then place those stack views within a vertical stack view. This achieves six evenly sized container views.
See WWDC 2015 video What's New in Cocoa Touch.
The problem with stack views is that they can be used to ensure even spacing between the arranged subviews, they don't ensure spacing before the first arranged view nor after the last arranged view. So, the kludge to get around that is to, for horizontal stack view, include two more zero width views (or zero height for vertical stack views). Then when you use even spacing on the stack view, it also give you what will appear to be spacing before and after all of the arranged subviews.
NSLayoutAttributeCenterX with multiple: Another technique involves defining the attribute:NSLayoutAttributeCenterX and attribute:NSLayoutAttributeCenterY attributes for your six buttons, but rather than using the constant values, use the multiplier field. This technique enjoys a little simplicity, but doesn't always render the desired effect, so I won't describe it unless it's something you definitely want to pursue. I've already entered tl:dr territory here.
Collection view: Another approach is to use a UICollectionView, which handles this scenario gracefully. It's well designed to let you layout cells in a grid.
Hardcoding values: For the sake of completeness, I'll note that you could simply specify specific values for A, B, C, and D (as well as the width and height constraints). You don't even have to worry about setting the E constraints, but rather just set the vertical center constraint of the middle two to their superview, and you're effectively done (because the spacing represented by E should be a natural result of the previous steps, assuming A=C and B=D). If you want to adjust these values on the basis of device size and/or orientation, you can then implement a viewWillLayoutSubviews to adjust the constants for these constraints according to the size of the view.
Update: I have a better solution that does not use spacers. Check it out here.
Ok, this can be achieved very quickly in IB. It's so so simple. Here's a diagram that will help illustrate.
Assume v1-6 are your buttons, and s1-5 are your spacers.
1) in IB control drag out all of the connections shown by the red lines.
2) shift click v1-6 and pin icon (looks like |-I-| ) set the width and height to a definite value. also, set the height and width to be equal.
3) shift select s1-4 (not 5) and set the height to equal. do not give it a definite height, since this should be calculated by the system. you might also need to set the widths of s1-4 to be equal, but don't give them a definite width.
4) control drag from the centre views to the leading and trailing edge and set the centre constraint.
So, you might think, ok, this should work now. It doesn't. Here's my app running in portrait with slightly different colors. Looks good. (Notice, you would make the spacers invisible once you get it setup).
But when I rotate, oops!
What's happening here? The problem is incredibly easy to solve once we understand what's gone wrong. What we want is for IB to not shrink our views. We want IB to make the spacers and the spaces to shrink and grow as necessary, but to leave our views alone. Basically, IB has shrunk the spacers down as far as it can in portrait and to attempt to make everything fit IB has shrunk our views. But we wanted IB to shrink the vertical spaces between views and spacers, not our views. The solution is so easy. All we have to do is adjust the priority of the vertical spaces and all is well. So, select the vertical spaces in IB and adjust the priority to 750. The vertical spacing lines will show as dashed. Done.
Ok, so here's everything as we expect it.
And with the spacers made clear:
I have several objects that are static in size that hug the top and bottom of the screen. However, there is a blank area in the middle of the screen that I would like to stretch with orientation change.
I have solved an issue by adding an extra clear view [paddedView] that can be stretched but wanted to know if there was an easier way to do this without the paddedView just with Visual Format Language.
#"V:|-20-[topLabel(40)]-15-[anotherTopLabel(40)]-[paddedView]-[bottomView(73)]|";
Instead of a view used only for padding, you can either specify a lower priority to a distance or specify the constraint as "greater than or equal to".
Since what you want is a flexible space, the last option sounds like the best one:
#"V:|-20-[topLabel(40)]-15-[anotherTopLabel(40)]-(>=15)-[bottomView(73)]|"
The number 15 is of course just an example.