Reputation: 303
I am trying to programmatically add a subview to a view inside a vertical stack view. It should try to maximize its size to fill its parent view but still keep its aspect ratio. I try to achieve it by adding an aspect ratio constraint with default priority. And add top, bottom, leading, trailing and centerX, centerY constraints with low priority. My prototype in interface builder works perfectly, but when I translate it into codes, the spacing at the borders are missing. Is there something missing in the programmatic version? Here is the code:
class ViewController: UIViewController {
@IBOutlet var greenView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let testView = UIView()
testView.backgroundColor = .red
greenView.addSubview(testView)
testView.translatesAutoresizingMaskIntoConstraints = false
let aspectRatioConstraint = NSLayoutConstraint(item: testView,
attribute: .width,
relatedBy: .equal,
toItem: testView,
attribute: .height,
multiplier: 6.0 / 10.0,
constant: 0)
let leadingConstraint = NSLayoutConstraint(item: testView,
attribute: .leading,
relatedBy: .equal,
toItem: greenView,
attribute: .leading,
multiplier: 1,
constant: 5)
let trailingConstraint = NSLayoutConstraint(item: testView,
attribute: .trailing,
relatedBy: .equal,
toItem: greenView,
attribute: .trailing,
multiplier: 1,
constant: 5)
let topConstraint = NSLayoutConstraint(item: testView,
attribute: .top,
relatedBy: .equal,
toItem: greenView,
attribute: .top,
multiplier: 1,
constant:5)
let bottomConstraint = NSLayoutConstraint(item: testView,
attribute: .bottom,
relatedBy: .equal,
toItem: greenView,
attribute: .bottom,
multiplier: 1,
constant: 5)
let centerXConstraint = NSLayoutConstraint(item: testView,
attribute: .centerX,
relatedBy: .equal,
toItem: greenView,
attribute: .centerX,
multiplier: 1,
constant: 0)
let centerYConstraint = NSLayoutConstraint(item: testView,
attribute: .centerY,
relatedBy: .equal,
toItem: greenView,
attribute: .centerY,
multiplier: 1,
constant: 0)
leadingConstraint.priority = .defaultLow
trailingConstraint.priority = .defaultLow
topConstraint.priority = .defaultLow
bottomConstraint.priority = .defaultLow
centerXConstraint.priority = .defaultLow
centerYConstraint.priority = .defaultLow
NSLayoutConstraint.activate([aspectRatioConstraint, leadingConstraint, trailingConstraint, topConstraint, bottomConstraint, centerXConstraint, centerYConstraint])
}
}
The broken version created programmatically: The top and bottom spacing are missing.
The expected look built with Interface Builder:
Here is the settings in IB:
Upvotes: 0
Views: 49
Reputation: 77423
I think what you want is to use greaterThanOrEqual
for top and leading, and lessThanOrEqual
for right and bottom.
Try it like this:
class ThiniumViewController: UIViewController {
@IBOutlet var greenView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let testView = UIView()
testView.backgroundColor = .red
greenView.addSubview(testView)
testView.translatesAutoresizingMaskIntoConstraints = false
let tvHeightConstraint = testView.heightAnchor.constraint(equalTo: testView.widthAnchor, multiplier: 10.0 / 6.0)
tvHeightConstraint.priority = .defaultHigh
let tvWidthConstraint = testView.widthAnchor.constraint(equalTo: greenView.widthAnchor, multiplier: 1.0)
tvWidthConstraint.priority = .defaultHigh
NSLayoutConstraint.activate([
// center testView in greenView
testView.centerXAnchor.constraint(equalTo: greenView.centerXAnchor),
testView.centerYAnchor.constraint(equalTo: greenView.centerYAnchor),
// constrain top and leading of redView to *at least* 5-points
testView.topAnchor.constraint(greaterThanOrEqualTo: greenView.topAnchor, constant: 5.0),
testView.leadingAnchor.constraint(greaterThanOrEqualTo: greenView.leadingAnchor, constant: 5.0),
// constrain height of testView to width of testView at 10:6 ratio at Priority 750
tvHeightConstraint,
// constrain width of testView to width of greenView at Priority 750
tvWidthConstraint,
// constrain bottom and trailing of redView to *at least* 5-points
testView.bottomAnchor.constraint(lessThanOrEqualTo: greenView.bottomAnchor, constant: -5.0),
testView.trailingAnchor.constraint(lessThanOrEqualTo: greenView.trailingAnchor, constant: -5.0),
])
}
}
As a side note, you may find it easier to write - and more readable - to construct your constraints as shown in my sample code.
Result:
Upvotes: 1