Stack view - but with "proportional" gaps - ios

Imagine a stack view with four items, filling something. (Say, filling the screen).
Notice there are three gaps, ABC.
(Note - the yellow blocks are always some fixed height each.)
(Only the gaps change, depending on the overall height available to the stack view.)
Say UISV is able to draw everything, with say 300 left over. The three gaps will be 100 each.
In the example, 9 is left over, so A B and C are 3 each.
However.
Very often, you want the gaps themselves to enjoy a proportional relationship.
Thus - your designer may say something like
If the screen is too tall, expand the spaces at A, B and C. However. Always expand B let's say 4x as fast as the gaps at A and B."
So, if "12" is left over, that would be 2,8,2. Whereas when 18 is left over, that would be 3,12,3.
Is this concept available in stack view? Else, how would you do it?
(Note that recently added to stack view, you can indeed specify the gaps individually. So, it would be possible to do it "manually", but it would be a real mess, you'd be working against the solver a lot.)

You can achieve that by following workaround. Instead of spacing, for each space add a new UIView() that would be a stretchable space. And then just add constraints between heights of these "spaces" that would constrain their heights together based on the multipliers you want, so e.g.:
space1.heightAnchor.constraint(equalTo: space2.heightAnchor, multiplier: 2).isActive = true
And to make it work I think you'd have to add one constraint that would try to stretch those spaces in case there is free space:
let stretchingConstraint = space1.heightAnchor.constraint(equalToConstant: 1000)
// lowest priority to make sure it wont override any of the rest of constraints and compression resistances
stretchingConstraint.priority = UILayoutPriority(rawValue: 1)
stretchingConstraint.isActive = true
The "normal" content views would have to have intrinsic size or explicit constraints setting their heights to work properly.
Here is an example:
class MyViewController: UIViewController {
fileprivate let stack = UIStackView()
fileprivate let views = [UIView(), UIView(), UIView(), UIView()]
fileprivate let spaces = [UIView(), UIView(), UIView()]
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
self.view.addSubview(stack)
// let stack fill the whole view
stack.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: self.view.topAnchor),
stack.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
stack.leftAnchor.constraint(equalTo: self.view.leftAnchor),
stack.rightAnchor.constraint(equalTo: self.view.rightAnchor),
])
stack.alignment = .fill
// distribution must be .fill
stack.distribution = .fill
stack.spacing = 0
stack.axis = .vertical
for (index, view) in views.enumerated() {
stack.addArrangedSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
// give it explicit height (or use intrinsic height)
view.heightAnchor.constraint(equalToConstant: 50).isActive = true
view.backgroundColor = .orange
// intertwin it with spaces
if index < spaces.count {
stack.addArrangedSubview(spaces[index])
spaces[index].translatesAutoresizingMaskIntoConstraints = false
}
}
// constraints for 1 4 1 proportions
NSLayoutConstraint.activate([
spaces[1].heightAnchor.constraint(equalTo: spaces[0].heightAnchor, multiplier: 4),
spaces[2].heightAnchor.constraint(equalTo: spaces[0].heightAnchor, multiplier: 1),
])
let stretchConstraint = spaces[0].heightAnchor.constraint(equalToConstant: 1000)
stretchConstraint.priority = UILayoutPriority(rawValue: 1)
stretchConstraint.isActive = true
}
}

Remarkably, #MilanNosáľ 's solution works perfectly.
You do not need to set any priorities/etc - it works perfectly "naturally" in the iOS solver!
Set the four content areas simply to 50 fixed height. (Use any intrinsic content items.)
Simply don't set the height at all of "gap1".
Set gap2 and gap3 to be equal height of gap1.
Simply - set the ratios you want for gap2 and gap3 !
Versus gap1.
So, gap2 is 0.512 the height of gap1, gap3 is 0.398 the height of gap1, etc.
It does solve it in all cases.
Fantastic!!!!!!!!!!
So: in the three examples (being phones with three different screen heights). In fact the relative heights of the gaps, is always the same. Your design department will rejoice! :)

Created: a gist with a storyboard example
The key here is Equal Heights between your arranged views and your reference view:
And then change the 'Multiplier` to your desired sizes:
In this example I have 0.2 for the main view sizes (dark grey), 0.05 within the pairs (black), and 0.1 between the pairs (light grey)
Then simply changing the size of the containing view will cause the views to re-size proportionally:
This is entirely within the storyboard, but you could do the same thing in code.
Note that I'm using only proportions within the StackView to avoid having an incorrect total size, (and making sure they add up to 1.0), but it should be possible to also have some set heights within the StackView if done correctly.

Related

Constraints on Child Container

I am adding a child view using the View.addChild method
The containing view is clearly 350 pixels. However, the child view takes up ALL the space of the containing view....so my idea is to force the child view to be smaller than its parent...but my code does not work. I can tell you that if I uncomment the two lines it almost works, but then the child view does not occupy the size that I want it to and it blocks other elements. Here is where I am:
child.view.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.layoutMarginsGuide
//child.view.topAnchor.constraint(equalTo: tableContainer.topAnchor).isActive = true
// child.view.bottomAnchor.constraint(equalTo: tableContainer.bottomAnchor).isActive = true
child.view.leftAnchor.constraint(equalTo: tableContainer.leftAnchor).isActive = true
child.view.rightAnchor.constraint(equalTo: tableContainer.rightAnchor).isActive = true
child.view.heightAnchor.constraint(equalToConstant: 250).isActive = true
self.addChild(child)
Let me state very clearly, my goal is to get the child view to 250 pixels. Thank you.
Your solution might look something like this.
child.view.translatesAutoresizingMaskIntoConstraints = false
let safeArea = view.layoutMarginsGuide
//Your left and right anchors tell the compiler exactly how wide the view should be.
//If you have it set to equal both then the view MUST be exactly the width of parent.
child.view.leftAnchor.constraint(equalTo: tableContainer.leftAnchor).isActive = true
child.view.rightAnchor.constraint(equalTo: tableContainer.rightAnchor).isActive = true
//Since you're defining your own height here you need to explicitly state where the
//view starts at. If you don't define a top, center, bottom or some other constraint
//it's impossible to know where to put the view.
child.view.heightAnchor.constraint(equalToConstant: 250).isActive = true
child.view.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
self.addChild(child)
The reason for this is that when creating anchors, you MUST have an X & Y anchors for all views, even logically. For example if you define a width of 100 and a height of 100 you've created a square, but where does that square go? You don't have an X or a Y in that example. However, if you define a width to match the parent view using the left and right anchor then it knows the width of the view just by the anchors on the left and right. The same principle applies to a top and bottom anchor. If you define a top and bottom anchor then it will be the size of the view (Given that you set them equalTo) and it will know the height.
In your instance, you've defined height of 250 however it doesn't know where to start at. Does it start at the top, middle, bottom, it doesn't have a clue because you haven't set it. The IDE is very literal with constraints and no obscurity will work.

How to display two textfields euqal space in single tableview cell with equal width, edges space

I want to display two textfields in single tableview cell along with some separator between of them both. Those, two textfields has equal space to edges and with the separator. I want to create this in programmatically in tableview cell class in Swift language.
I want this to be fit in all dimension devices.
Note : I am not using autolayout.
Any suggestions?
I highly recommend you to use UIStackViews as it sounds like a perfect use case for your question.
Definition of a UIStackView in the Documentation:
A streamlined interface for laying out a collection of views in either
a column or a row.
It would take care of the resizing behaviour of your views and you could also easily control the margins and the spacing between your elements.
You can use UIStackViews with storyboards or programmatically depending on what you are looking for.
You may also want to read this guide in the documentation to learn more about UIStackViews.
How to use:
Say you have two text fields, textFieldA, textFieldB and your separator view named separatorView.
Here is how you could do setup your UIStackViews programmatically inside your UITableViewCell subclass:
// Create and configure your stack view
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.horizontal
stackView.distribution = UIStackViewDistribution.fillProportionally
stackView.alignment = UIStackViewAlignment.fill
stackView.spacing = 20.0
// Add your textfields and your separator view to the stack view
stackView.addArrangedSubview(textFieldA)
stackView.addArrangedSubview(separatorView)
stackView.addArrangedSubview(textFieldB)
stackView.translatesAutoresizingMaskIntoConstraints = false
// Add your stack view:
self.addSubview(stackView)
// Configure the constraints for your stack view
// (Of course you can set up your stack view the way you want and you don't have to absolutely use constraints, but it's just for the example)
stackView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -20).isActive = true
stackView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 20).isActive = true
stackView.topAnchor.constraint(equalTo: self.topAnchor, constant: 20).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 200.0).isActive = true
It will save you a lot of time as you don't need to add constraints everywhere for all your views, but only for the stack view. The stack view will then take care of its arranged subviews.
One thing you may need in your case, is changing the hugging and compression resistance values for your views, to make sure it looks great on any screens.
As you can see you there are a lot of properties that you can set on your stack view, so just feel free to try different values and set it up the way you like!
Use stack view with axis horizontal and distribution is equal to fillProportionally

UIStackView proportional layout with only intrinsicContentSize

I'm experiencing problems with layout of arranged subviews in UIStackView and was wondering if someone could help me understand what's going on.
So I have UIStackView with some spacing (for example 1, but this does not matter) and .fillProportionally distribution. I'm adding arranged subviews with only intrinsicContentSize of 1x1 (could be anything, just square views) and I need them to be stretched proportionally within stackView.
The problem is that if I add views without actual frame, only with intrinsic sizes, then I get this wrong layout
Otherwise, if I add views with frames of the same size, everything works as expected,
but I really prefer not to set view's frame at all.
I'm pretty sure that this is all about hugging and compression resistance priority, but can't figure out what right answer is.
Here is an Playground example:
import UIKit
import PlaygroundSupport
class LView: UIView {
// If comment this and leave only intrinsicContentSize - result is wrong
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 1, height: 1))
}
// If comment this and leave only convenience init(), then everything works as expected
public override var intrinsicContentSize: CGSize {
return CGSize(width: 1, height: 1)
}
}
let container = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
container.backgroundColor = UIColor.white
let sv = UIStackView()
container.addSubview(sv)
sv.leftAnchor.constraint(equalTo: container.leftAnchor).isActive = true
sv.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true
sv.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
sv.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
sv.translatesAutoresizingMaskIntoConstraints = false
sv.spacing = 1
sv.distribution = .fillProportionally
// Adding arranged subviews to stackView, 24 elements with intrinsic size 1x1
for i in 0..<24 {
let a = LView()
a.backgroundColor = (i%2 == 0 ? UIColor.red : UIColor.blue)
sv.addArrangedSubview(a)
}
sv.layoutIfNeeded()
PlaygroundPage.current.liveView = container
This is obviously a bug in the implementation of UIStackView (i.e. a system bug).
DonMag already gave a hint pointing in the right direction in his comment:
When you set the stack view's spacing to 0, everything works as expected. But when you set it to any other value, the layout breaks.
Here's the explanation why:
ℹ️ For the sake of simplicity I will assume that the stack view has
a horizontal axis and
10 arranged subviews
With the .fillProportionally distribution UIStackView creates system-constraints as follows:
For each arranged subview, it adds an equal width constraint (UISV-fill-proportionally) that relates to the stack view itself with a multiplier:
arrangedSubview[i].width = multiplier[i] * stackView.width
If you have n arranged subviews in the stack view, you get n of these constraints. Let's call them proportionalConstraint[i] (where i denotes the position of the respective view in the arrangedSubviews array).
These constraints are not required (i.e. their priority is not 1000). Instead, the constraint for the first element in the arrangedSubviews array is assigned a priority of 999, the second is assigned a priority of 998 etc.:
proportionalConstraint[0].priority = 999
proportionalConstraint[1].priority = 998
proportionalConstraint[2].priority = 997
proportionalConstraint[3].priority = 996
...
proportionalConstraint[n–1].priority = 1000 – n
This means that required constraints will always win over these proportional constraints!
For connecting the arranged subviews (possibly with a spacing) the system also creates n–1 constraints called UISV-spacing:
arrangedSubview[i].trailing + spacing = arrangedSubview[i+1].leading
These constraints are required (i.e. priority = 1000).
(The system will also create some other constraints (e.g. for the vertical axis and for pinning the first and last arranged subview to the edge of the stack view) but I won't go into detail here because they're not relevant for understanding what's going wrong.)
Apple's documentation on the .fillProportionally distribution states:
A layout where the stack view resizes its arranged views so that they fill the available space along the stack view’s axis. Views are resized proportionally based on their intrinsic content size along the stack view’s axis.
So according to this the multiplier for the proportionalConstraints should be computed as follows for spacing = 0:
totalIntrinsicWidth = ∑i intrinsicWidth[i]
multiplier[i] = intrinsicWidth[i] / totalIntrinsicWidth
If our 10 arranged subviews all have the same intrinsic width, this works as expected:
multiplier[i] = 0.1
for all proportionalConstraints. However, as soon as we change the spacing to a non-zero value, the calculation of the multiplier becomes a lot more complex because the widths of the spacings have to be taken into account. I've done the maths and the formula for multiplier[i] is:
Example:
For a stack view configured as follows:
stackView.width = 400
stackView.spacing = 2
the above equation would yield:
multiplier[i] = 0.0955
You can prove this correct by adding it up:
(10 * width) + (9 * spacing)
= (10 * multiplier * stackViewWidth) + (9 * spacing)
= (10 * 0.0955 * 400) + (9 * 2)
= (0.955 * 400) + 18
= 382 + 18
= 400
= stackViewWidth
However, the system assigns a different value:
multiplier[i] = 0.0917431
which adds up to a total width of
(10 * width) + (9 * spacing)
= (10 * 0.0917431 * 400) + (9 * 2)
= 384,97
< stackViewWidth
Obviously, this value is wrong.
As a consequence the system has to break a constraint. And of course, it breaks the constraint with the lowest priority which is the proportionalConstraint of the last arranged subview item.
That's the reason why the last arranged subview in your screenshot is stretched.
If you try out different spacings and stack view widths you'll end up with all sorts of weird-looking layouts. But they all have one thing in common:
The spacings always take precedence. (If you set the spacing to a greater value like 30 or 40 you'll only see the first two or three arranged subviews because the rest of the space is fully occupied by the required spacings.)
To sum things up:
The .fillProportionally distribution only works properly with spacing = 0.
For other spacings the system creates constraints with an incorrect multiplier.
This breaks the layout as
either one of the arranged subviews (the last) has to be stretched if the multiplier is smaller than it should be
multiple arranged subviews have to be compressed if the multiplier is greater than it should be.
The only way out of this is to "misuse" plain UIViews with a required fixed-width constraint as spacings between the views. (Normally, UILayoutGuides were introduced for this purpose but you cannot even use those either because you cannot add layout guides to a stack view.)
I'm afraid that due to this bug, there is no clean solution to do this.
As of Xcode 9.2 at least, the playground provided works as intended provided the initializer and the intrinsic content size are both commented out. In that case, the proportional filling works as expected, even with spacing > 0
This is an example with spacing = 5
That seems to make sense because the arranged subviews have no intrinsic content size and the StackView determines their widths to proportionally fill the designated axis.
If only the initializer is enabled (and not the intrinsic content size override), then I get this, which doesn't match the comment in the playground, so I guess this behaviour must have changed since the question was posted:
I don't understand that behaviour, because it would seem to me that setting the frame manually should be ignored when using Auto Layout.
If only the intrinsic content size override is enabled (and not the initializer) then I get the problematic image that originated this post (here with spacing = 5):
Essentially, the design now is conflicting and can't be realized, because views want to be 1 point wide, due to specified intrinsic content size. The total space here should be
24 views * 1 point/view + 23 spaces * 5 points/space = 139 < 300 = sv.bounds.width
with the last arranged view's constraint broken due to lowest priority as pointed out by Mischa.
Upon pixel-per-pixel inspection the first 23 views above are wider than 1 pixel though, 2 or 3 pixels actually, except for the last one, thus the math doesn't quite match, I don't know why, possibly rounding up of decimal numbers?
For reference, this is what it looks like in that case with intrinsic content size of width 5, still failing to satisfy the constraints.

UIScrollView how do you constrain a sub view that acts a container to all the other views?

so as you will see below I have a scrollview and I want to add it the the UIViewControllers root view. When I have it constrained to the top, right, bottom, and left I expect to see the red color take up the whole screen. This obviously works, but I want to add a subview to the scrollview that will wrap all the child views. How would I go about doing that?
I have added the view and I have set the same constraints except this time they are set from the wrapper view to the bounds of the UIScrollView, and the blue background color doesn't show anywhere. Also feel free to point out if this is a bad idea, but I thought I could just have it be constrained to the bottom and it will automatically extend the scrollviews content size as needed. This seems to work when I had all the subviews in the scrollview without a wrapper and the last view would extend the content size.
scrollView = UIScrollView(frame: view.bounds)
scrollView?.showsVerticalScrollIndicator = true
scrollView?.backgroundColor = .red
scrollView?.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView!)
scrollView?.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView?.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView?.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView?.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
//setup wrapper view
let subviewWrapper = UIView()
subviewWrapper.translatesAutoresizingMaskIntoConstraints = false
scrollView?.addSubview(subviewWrapper)
subviewWrapper.backgroundColor = .blue
subviewWrapper.topAnchor.constraint(equalTo: (scrollView?.topAnchor)!).isActive = true
subviewWrapper.leftAnchor.constraint(equalTo: (scrollView?.leftAnchor)!).isActive = true
subviewWrapper.rightAnchor.constraint(equalTo: (scrollView?.rightAnchor)!).isActive = true
subviewWrapper.bottomAnchor.constraint(equalTo: (scrollView?.bottomAnchor)!).isActive = true
Actually this is a very good idea. I always set up my scrollViews this way. I usually call the view contentView, but it is the same idea.
You're almost there. You haven't yet given Auto Layout anything to go on to figure out the size of your subviewWrapper. The constraints you've set so far pin the subviewWrapper to the edges of the scrollView's content area, but this just establishes the fact that as the subviewWrapper grows, the content size of the scrollView will expand. Currently your subviewWrapper has 0 width and 0 height which is why you see no blue.
Below are 3 examples of how you might establish the size of your subviewWrapper.
Note: Each of the following examples is completely independent. Look at each one separately and as you try them, remember to delete the constraints added by the previous example.
Example 1: Make subviewWrapper 1000 x 1000:
Set constraints to make your subviewWrapper 1000 x 1000 and you will see the blue and it will scroll in both directions.
subviewWrapper.widthAnchor.constraint(equalToConstant: 1000).isActive = true
subviewWrapper.heightAnchor.constraint(equalToConstant: 1000).isActive = true
Example 2: Vertical only scrolling with content size 2X of scrollView height:
If you set the width of your subviewWrapper to be equal to the width of the scrollView then it will only scroll vertically. If you set the height of subviewWrapper to 2X the height of scrollView, then your blue area will be twice the height of the scrollView.
subviewWrapper.widthAnchor.constraint(equalTo: scrollView!.widthAnchor, multiplier: 1.0).isActive = true
subviewWrapper.heightAnchor.constraint(equalTo: scrollView!.heightAnchor, multiplier: 2.0).isActive = true
Example 3: Size of subviewWrapper set by its subviews:
You can also establish the size of your subviewWrapper by adding subviews to it that are fully specified in size and connected in a chain from the top of subviewWrapper to the bottom, and from side to side. If you do this, Auto Layout will have enough information to compute the size of your subviewWrapper
In this example, I've added a yellow 600 x 600 square to the subviewWrapper and set it 100 points from each edge. Without having explicitly set a size for subviewWrapper, Auto Layout can figure out that it is 800 x 800.
let yellowSquare = UIView()
yellowSquare.translatesAutoresizingMaskIntoConstraints = false
yellowSquare.backgroundColor = .yellow
subviewWrapper.addSubview(yellowSquare)
yellowSquare.widthAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.heightAnchor.constraint(equalToConstant: 600).isActive = true
yellowSquare.topAnchor.constraint(equalTo: subviewWrapper.topAnchor, constant: 100).isActive = true
yellowSquare.leadingAnchor.constraint(equalTo: subviewWrapper.leadingAnchor, constant: 100).isActive = true
yellowSquare.trailingAnchor.constraint(equalTo: subviewWrapper.trailingAnchor, constant: -100).isActive = true
yellowSquare.bottomAnchor.constraint(equalTo: subviewWrapper.bottomAnchor, constant: -100).isActive = true

How do i make the constraints resize the buttons correctly?

I've added a stoplight image and red, yellow, and green buttons. I want to have the buttons resize to iPhone 4S and iPhone 6S screens, but the buttons either disappear off the page or are the wrong size for the iPhone 4S. I thought the number of point would resize proportionately, but it appears it does not. Any help would be appreciated, I really want to understand constraints but I am just not getting it! Normally I would just do a x-position/screensize, y-position/screensize to relocated it, but this could be noticeably too long.
Here is the constraints of the latest incorrect location. When I try to select the stoplight image, it won't provide a constraint for the leading and trailing edge to the stoplight image.
The yellow button is placed against the stoplight image, but it won't resize.
The easiest solution would be to give all images fixed values for their width and height constraints. Then you can align the spotlightImage in the superview as you wish and define the alignment of the circle images relative to the stoplight image.
However, if you would like to stretch the width of the stoplight image depending on the width of the screen, this is a complex problem. I played around a bit trying to define all constraints in storyboard, but could not come up with a proper solution. What one ideally would like to do, for example, is define the centreX of the circles proportionally to the spotlight image's width. Similarly for the y position. Unfortunately this is not possible.
In code one have a little bit more control. Here is a solution that will work. It is not pretty, because you are actually recalculating the width of the spotlightImage, but it works :-)
class ViewController: UIViewController {
lazy var stopLightImageView: UIImageView = {
return UIImageView(image: UIImage(named:"stopLight"))
}()
lazy var circleImageView: UIImageView = {
return UIImageView(image: UIImage(named:"circle"))
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
//Values at start. This is used to calculate the proportional values, since you know they produce the correct results.
let stoplightStartWidth: CGFloat = 540
let stoplightStartHeight: CGFloat = 542
let circleStartWidth: CGFloat = 151
let circleStartLeading: CGFloat = 231
let circleStartTop: CGFloat = 52
let screenWidth = UIScreen.mainScreen().bounds.size.width
let stoplightMargin: CGFloat = 20
self.view.addSubview(stopLightImageView)
stopLightImageView.translatesAutoresizingMaskIntoConstraints = false
//stoplightImage constraints
stopLightImageView.leadingAnchor.constraintEqualToAnchor(self.view.leadingAnchor, constant: stoplightMargin).active = true
stopLightImageView.trailingAnchor.constraintEqualToAnchor(self.view.trailingAnchor, constant: -stoplightMargin).active = true
stopLightImageView.centerYAnchor.constraintEqualToAnchor(self.view.centerYAnchor, constant: 0).active = true
stopLightImageView.heightAnchor.constraintEqualToAnchor(stopLightImageView.widthAnchor, multiplier: stoplightStartWidth/stoplightStartHeight).active = true
self.view.addSubview(circleImageView)
circleImageView.translatesAutoresizingMaskIntoConstraints = false
//circle constraints
circleImageView.widthAnchor.constraintEqualToAnchor(stopLightImageView.widthAnchor, multiplier: circleStartWidth/stoplightStartWidth).active = true
circleImageView.heightAnchor.constraintEqualToAnchor(circleImageView.widthAnchor, multiplier: 1).active = true
let stoplightWidth = screenWidth - 2*stoplightMargin
let stoplightHeight = stoplightWidth * stoplightStartHeight/stoplightStartWidth
circleImageView.leadingAnchor.constraintEqualToAnchor(stopLightImageView.leadingAnchor, constant: stoplightWidth*circleStartLeading/stoplightStartWidth).active = true
circleImageView.topAnchor.constraintEqualToAnchor(stopLightImageView.topAnchor, constant: stoplightHeight*circleStartTop/stoplightStartHeight).active = true
}
}
Constraints are tricky, and it looks like you have a lot going on there. It's hard to tell you exactly what to do for this so, here's what I would try to do if I was having this issue(hopefully one works for you):
Set the images in the Attributes Inspector to either Aspect Fit or Redraw... That should fix your issue with them being different shapes.
Also look through the list of constraints to see if one relies on another, (for example the red and yellow seem to have similar constraints). If they rely on each other, ensure to satisfy any constraints that aren't yet - based off of the "parent" image.
Select everything and set to "Reset to Suggested Constraints". Build and run. If that doesn't fix it then there's only a few things left you can do.
Remove all the constraints on every object. Start with the black image and add missing constraints... or set it to "Center Horizontally in Container". Right click and drag the image or asset to your "view" or to the yellow "First" circle located above.
Hopefully this helps.

Resources