How to make a square view resize with its superview using auto layout
You have a view that needs to expand to fill its container while maintaining its aspect ratio. This is a common pattern with Auto Layout.
The trick is to use two constraints for leading/trailing/top/bottom:
=10
at low priority>=10
at required priority.
Putting it all together, you have:
Aspect Ratio 1:1
Center X/Y in Superview
Leading/Trailing/Top/Bottom to Superview = 10 (at 750 priority)
Leading/Trailing/Top/Bottom to Superview >= 10 (at 1000 priority)
There are also a couple of things to consider with UIImageView:
UIImageView will have an intrinsic content size based on the image that it is displaying, so you'll want to ensure that its Content Hugging Priority is lower than the 750 priority you use for the
=10
constraints.UIImageView.contentMode
determines how the underlying image is sized relative to the size of the UIImageView. By default, it's set toUIViewContentModeScaleToFill
.
I had success using this configuration.
What I did was first add constraints for center X. (Ignore the center Y one even though it's in my screenshot. It will break regardless because of step 2.)
Then I added a top and bottom constraint
Finally I added a aspect ratio constraint
When I animated this, the box scaled as a square correctly. If you want I can upload the Test project.
container view
----------------------------------------------
| | |
| >=10 |
| imageView | |
| ---------------------------- |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
|- >=10 -|---------- 1:1 -----------|- >=10 -|
| | | | |
| | | | |
| | | | |
| | | | |
| ---------------------------- |
| | |
| >=10 |
| | |
----------------------------------------------
**If you want you can specify the imageView's height or width as well with a lower priority constraint.
In code, for realz. Note: the weird CGFloat.greatestFiniteMagnitude
is necessary (or some larger number anyway) to get the other constraints to pick up. Enjoy.
extension UIView {
func constrainAsSquare(container: UIView, multiplier: CGFloat) {
translatesAutoresizingMaskIntoConstraints = false
centerXAnchor.constraint(equalTo: container.centerXAnchor).isActive = true
centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
widthAnchor.constraint(equalToConstant: .greatestFiniteMagnitude).activate(with: .defaultLow)
heightAnchor.constraint(lessThanOrEqualTo: container.heightAnchor, multiplier: multiplier).activate(with: .defaultHigh)
widthAnchor.constraint(lessThanOrEqualTo: container.widthAnchor, multiplier: multiplier).activate(with: .defaultHigh)
widthAnchor.constraint(equalTo: heightAnchor).activate(with: .required)
}
}
extension NSLayoutConstraint {
@discardableResult
func activate(with priority: UILayoutPriority) -> NSLayoutConstraint {
self.priority = priority
isActive = true
return self
}
}