I am struggling to align 5 items in a row using auto layout as shown in the storyboard.
I can align the 3 items highlighted below but the 2 inbetween just don't want to align.
I have tried pinning the items to the buttons next to each of the items and setting the size but they refuse to align properly. Have also tried setting the 2 misplaced buttons to the leading and trialing container as shown below but these still do not align properly (or even closely).
Am I doing something wrong?
The way to do this is with spacer views. You need to add a UIView between each of your views and give them each horizontal spacing constraints to the view on either side. You'll need to edit these constraints so they are between the spacer's leading or trailing edge, and the view's centerX (the constant value should be 0). Give the spacers equal widths (but no fixed width), a fixed height, and a clear background color. You views on the left and right sides should have constraints to their closest edge, but the center view shouldn't have a centerX constraint (it's not needed). This will cause the views to be equally spaced in all screen sizes, and in both orientations.
Your best bet might be to create another superview (UIView) within which your button items are located. Then you can align your buttons more fluidly within that superview that encapsulates only those buttons rather than just wrestling with the constraints within the card superview, which it seems you are doing. Just make the background of that UIView clear, place the buttons on it, and it will appear exactly the same and you'll have a better way to anchor your views. This could also come in handy when you're targeting the functionality of just those buttons so you can grab them from the subviews of the button bar superview.
Related
I'm trying to make a row of buttons on the bottom of the screen and the buttons are images. I was having aspect ratio issues so a few sites recommended making image views and simply put transparent buttons on top of them. I've placed six image views in a stack view and constrained it to the bottom. I thought it would make sense to put six buttons in another stack view and then also constrain it to the bottom, but it always constrains to the nearest neighbor so now the images are actually on the bottom of the screen, but the transparent buttons are vertically sitting above them. Is it possible to have both constrained to the bottom of the screen so that they completely overlap? Thanks!
It is very much possible, from what you have written I assume that your second stackView is pinned to the top of First stackView. Change that constraint to bottom of your superView. This would be enough for your requirement.
Also Instead of using two different stackViews, I would recommend doing this in one single StackView, which will 6 UIViews inside and each UIView will have UIImageView and UIButton inside.
(Thanks, Sujith Chandran and Neil Hiddink for pointing the way!!)
Here is my solution: After selecting the bottom constraint for the stack view that contained the transparent button overlay, I went to the size inspector and changed the "Second Item" to superview, opened second item again and selected bottom, and then set Constant to 0. Eventually I had to change the value of constant to -70 to get the desired position because I previously set the button images to a height of 70.
Say I have 2 views (labels or textfields) with varying heights positioned above each other (not on top). I would like the group centred vertically in the superview.
I know I could use a dummy spacer view above and below and set equal height in storyboard. I'm no expert, but this doesn't seem right to me.
Can it be done in storyboard by setting the top constraint for the top view equal to the bottom constraint of the bottom view?
If your subviews' heights will change at runtime (either because you change the number of lines of text in them, or because you support Dynamic Type like a good iOS app should), then no, you cannot do this with just constraints. You either need an extra view or you need to change the constraint constants from code. If you go with an extra view (which I recommend), you can do it with a single extra view.
The simplest solution, if your deployment target is iOS 9 or later, is to put your subviews into a vertical UIStackView and center the UIStackView in the superview. You might not need any constraints directly on the subviews depending on the layout you want.
If you can't use a stack view, you can use a spacer view. First, set up vertical spacing constraints between your subviews to glue them together with whatever padding you want. Then add a spacer view next to the subviews. Set the spacer view to hidden. (Hidden views still participate in layout.) Constrain the spacer's top edge to your top subview's top edge, and constrain the spacer's bottom edge to your bottom subview's bottom edge. Then constrain the spacer to be vertically centered in its container (which should be the superview).
You also need to add horizontal constraints on the spacer to keep Xcode from complaining, but the horizontal size and position don't really matter since the spacer is hidden. So just put it somewhere convenient.
Here's the final set of constraints and layout:
The spacer is the skinny pink view.
You can achieve it easily via center vertically, control and drag the object you want to centre in Storyboard, choose center vertically in container. This way you can centre these subviews in superView individually. If you want to centre these subviews together, you can create another containerView to contain these views(labels, textFields), then centre the containerView in superView.
Hope this help:-)
Control and drag in Storyboard
Center three subView individually.
Center them together with a container view
My problem is slightly different from other's 'Swift UIScrollView' problems when using auto layout:
Problem:
Unlike others, when I run my app, it scrolls. My problem is that the scroll cuts off the bottom 20-30% of the content. I can pull to see the buttons did build and are down there, but when I let it go the scroll snaps back to a false bottom which cuts off my content!!! I've been trying, for days, to fix it to scroll the entire height but it continues to cut off!!
Description of app:
I used auto layout to layout 6 buttons and labels. Each button a rectangular image, with a label directly beneath it. (sorry, the site won't let me post pictures!)
I have my views arranged like this:
MainView > ScrollView > ContentView > Buttons & Labels
I have my contentView pinned to my ScrollView and my ScrollView pinned to my MainView. My buttons and labels all have constraints that are building correctly, to create a list that looks like:
Rectangular button
Label beneath it
Spacing
Rectangular button
Label beneath it
Spacing
Etc.
Can anyone tell me why I can't scroll the full length of the view?
Your description of how your items are constrained is vague, so I'm going to list all of the constraints you need to make this work. Then you can compare what you have to what you need and adjust accordingly.
Your ScrollView should be pinned on all 4 sides to the MainView. (This isn't absolutely necessary. You can constrain your ScrollView however you want, but make sure it can grow as the device and/or orientation changes).
Your ContentView should be pinned on all 4 sides to the ScrollView with offsets of 0.
Since you want your ScrollView to scroll vertically only, constrain the width of the ContentView to the width of the ScrollView using an Equal Widths constraint. To do this, in the Document Outline view, Control-drag from your ContentView to your ScrollView and select Equal Widths from the pop up.
The height of ContentView will be set by the sum total height of everything in it. In order for this to work, your topmost button needs to be pinned to the top of the ContentView, all of your buttons and labels should be pinned to their nearest neighbors, and the bottommost label should be pinned to the bottom of the ContentView. In addition, all of your buttons and labels should have constraints for their widths and heights. I would suggest setting an explicit width constraint and explicit height constraint for your buttons and centering them horizontally in the ContentView. For your labels, set an explicit height constraint and pin the left and right edges to the ContentView.
If you have these constraints and no other ones, your ContentView will be properly sized.
Using contentView, like you said, usually fixes the issue. So chances are you need to take a second look at your contraints. Maybe try this solution in a clean/new project to see that it works. (it does work). My guess would be that some of your constraints conflict each other.
Otherwise I think it would be a good idea to setContentSize of your scrollView in your viewDidLoad.
Another hack would be to place 2 UI objects with their alphas set to zero on the right top corner and left bottom corner. This would hint scrollView to set its contentSize.
I need to layout a series of buttons horizontally across the screen so that they're equally spaced across the screen, not equally spaced between themselves. An example is having 3 buttons there they are evenly spaced so that the first button is centered at 25% of the screen width and others are 50%, 75% of the screen width.
This is relatively straightforward manually placing them in x,y coordinates but I'm trying to avoid mixing approaches.
The prevailing recommendation is to use spacers (UIView) between the buttons and put a constraint to make the spacers equal. This does not work if the buttons being spaced are of potentially different sizes. Say there are 3 buttons labeled, "A", "B", "ReallyLong". I still want them centered, with "B" in the middle of the screen. Equal spacers leaves equal spacing between them but not evenly distributed buttons. ReallyLong takes up too much room and B is not centered
You can create a constraint to align the centerX of a button to its container. Then, edit that constraint so that the centerX of the button is equal to the container's trailing attribute, with a multiplier of 0.25, 0.5, or 0.75 (and a 0 constant). To do this most naturally, you may need to tell Xcode to swap the first and second items, so that Button.CenterX equals Superview.Trailing (with multiplier) rather than the other way around.
Apple has explained it really well.
Creating Equal Spacing Between Views
To lay out several views that are proportionally spaced based on the orientation of a device, create spacer views between the visible views. Set the constraints of these spacer views correctly to ensure that the visible views are able to stay spaced apart based on the orientation of the device.
The following example uses the steps in the above task to show how to position two views proportionally spaced. The spacer views are annotated for the example, but are normally left empty with no background. First, create the two views and place them in the storyboard.
Add the three spacer views—one to the left of the leftmost view, one between the two views, and one to the right of the rightmost view. The spacer views don’t have to be the same size at this time because their size will be set through constraints.
Create the following constraints for the spacer views:
Constrain the width of spacer view 2 and spacer view 3 to be equal to
the width of spacer view 1.
Constrain the width of spacer view 1 to be greater than or equal to
the minimum desired width.
Create a Leading Space to Container constraint from spacer view 1 to
the container.
Create a Horizontal Spacing constraint from spacer view 1 to view 1.
Set this constraint to be a less-than-or-equal-to constraint with a
priority of 1000.
Create Horizontal Spacing constraints from spacer view 2 to view 1
and view 2. Set these constraints to be a less-than-or-equal-to
constraint with a priority of 999.
Create a Horizontal Spacing constraint from spacer view 3 to view 2.
Set this constraint to be a less-than-or-equal-to constraint with a
priority of 1000.
Create a Trailing Space to Container constraint from spacer view 3 to
the container.
These constraints create two visible views and three invisible views (spacer views). These spacer views automatically resize as the orientation of the device changes, keeping the visible views proportionally spaced, as shown in the following two figures:
I think I solved it. I have earlier been able to get even spaces BETWEEN buttons by inserting spacers between each button and setting the spacer widths to be equal. And addition is to also specify that the button widths are equal. This seems to be working well. All the text is centered in the appropriate place.
For 3 buttons, here's the one visual constraint that seems to do it.
Constraint = "H:|[spacer0(>=0)][button0][spacer1(==spacer0)][button1(==button0)][spacer2(==spacer0)][button2(==button0)][spacer3(==spacer0)]|"
With the above text it looks like this, which is what I was looking for. The middle button is centered, the right button is centered on the right third of the screen:
I have a UIViewController on my storyboard that has 2 subviews side-to-side horizontally. I added constraints to fix the leading and trailing edges to a constant (20 pts), and another constraint to keep the widths equal. If I assume the following, it should be possible to calculate what the width of each subview will need to be:
the subviews do not overlap
there are no other views present (horizontally, at least)
the width of the screen (the superview) is known
However, XCode gives me a warning that my views are horizontally ambiguous. I'm guessing that means that XCode is not making one of these assumptions, but which one is it? And is there a way for me to instruct XCode to make that assumption?
EDIT: Okay, played with it a bit and got the warning to go away, but it looks like it's not making the first assumption - it's just setting each subview's width to superview.width - 40, and happily burying one view underneath the other. So the question is how to I stop them from overlapping?
EDIT 2: Okay, my actual screen is a lot more complicated than my simple example. Here's what I got:
So in this setup I have 4 views that are vertically and horizontally staggered. I want the blue, red, and purple views to all be the same subview.frame.size.width = superview.width - 60. The blue and purple are lined up in the left column, and the red is alone in the right column, and all the gaps (between the two columns and between each column and it's nearest edge) are at a constant (20 pts). These 3 tables have a variable height, which I will be setting programmatically as described in James's answer here. At the bottom is a pink view that stretches the width of the screen (minus gaps), and sits at a constant 20 pts below either the purple or the red view, whichever is lower (which I'm attempting to do by giving it a spacing constraint of >= 20 to each view, and I hope that it will pick exactly 20 for one of them). Since all of the heights are dynamic and may not necessarily fit on the screen at the same time, I made their superview a UIScrollView instead of the normal UIView.
When all is said and done, I'm still getting a warning that all 4 of my views are horizontally ambiguous, and that the pink bar is vertically ambiguous. I think it's having trouble realizing what is supposed to go next to what, which is why it thinks it's horizontally ambiguous. And I think it's not picking to place the pink bar exactly 20 pts below either the purple or red views, which is why it thinks it's vertically ambiguous. Can anyone confirm or deny any of these suspicions? Or suggest a way around it? When I run it in the end, I just get this (I made the background of the scroll view yellow, which you can't tell in the storyboard screenshot):
Vertically Ambiguous
Okay, I think I've solved the vertical ambiguous part. I added two vertical constraints between the pink and purple views and two vertical constraints between the pink and red views. For each pair, the first constraint is that the spacing between them must be > 20 pts, and it has 1000 priority. The second constraint is that the spacing is = 20 pts, but it only has an 800 priority.
For example, if the bottom of the purple view ends up being lower than the bottom of the red view (as it is in my first screenshot), Xcode should try to set the vertical distance between the pink and red views = 20, but it will realize that that conflicts with condition that the space between the purple and pink being >= 20. Since the >= constraint has higher priority, the = constraint will be ignored. Now, when Xcode looks at the constraint that the spacing between the purple and pink views being = 20, it checks that against the constraint that the pink and red must be separated by at least 20. Since the bottom of the red view is higher than the bottom of the purple view, the >= 20 constraint between the red and the pink still passes.
So TL;DR, you can set up a view to have a spacing at a given value (x) from the most extreme of multiple views by giving it a >= x constraint with 1000 priority and giving it a = x constraint with <1000 priority for each view you are considering - and my vertical ambiguity problem has been solved. I do not yet have a solution for the horizontal ambiguity for all 4 of the views.
Horizontally Ambiguous
Okay, I got the horizontally ambiguous part fixed now as well. What it boils down to is that constraints in scroll views (and therefore table views) work differently than they do for any other kind of view. Here's what the step-by-step looks like.
Place the UIScrollView
Place a UIView into the UIScrollView to serve as a "contentView" for that scroll view
Add constraints to pin the contentView to all 4 corners of the scroll view AND pin it's width and height (so 6 constraints between the contentView and it's superview - 2 more than usual). Note that the width and the height can be pinned to something much larger than the normal screen size, which is probably why you are using a scroll view to begin with.
Add all of your other views you want in the UIScrollView (UIButtons, UILabels, etc. - I'm just going to assume UILabel from here on so I don't have to type as much, but any kind of UIView subclass will work) as subviews of the contentView, NOT directly as subviews of the UIScrollView
With this setup, the UILabels that are given constraints to their superview will constrain to the contentView, which has a defined size, so nothing is ambiguous.
Alternatively, if you want to fix the sizes of your UILabels (or dynamically calculate them, depending on the functionality of your app) and let the contentView expand to hold them:
Place the UIScrollView
Place a UIView into the UIScrollView to serve as a "contentView" for that scroll view
Add constraints to pin the contentView to all 4 corners of the scroll view AND pin it's width and height
create an outlet for the width constraints on the contentView (let's say we name it contentViewWidthConstraint)
place the UILabels
fix the sizes of the UILabels
create an outlet for the width constraints on the UILabels
Then in the code for viewWillLayoutSubviews
add up the widths of all of the UILabels and any gaps you want between them (as a CGFloat, which I'll call totalWidth)
set contentViewWidthConstraint.constant = totalWidth
And you're good to go! Note that I assumed you were setting the width in most of this example, but it should be just as applicable to height.
The problem is that many different widths of the two views will satisfy the constraints that you've set up. Here are two examples (I drew the shapes stacked vertically to make it easier to see the overlap example):
You can add a horizontal space constraint with a value of 0.