I have five UIView instances constrained using autolayout. When in portrait mode they fit into the screen, but when in landscape mode the screen becomes to small to contain all the views.
I know one way to solve this by creating two constraints and active one in portrait mode and deactivate the other and vice versa in landscape mode like this:
class ViewController : UIViewController{
var portraitHeight : NSLayoutConstraint!
var landscapeHeight : NSLayoutConstraint!
viewDidLoad(){
super.viewDidLoad()
portraitHeight = firstView.heightAnchor.constraint(equalToConstant: 88)
landscapeHeight = firstView.heightAnchor.constraint(equalToConstant: 68)
}
override func viewWillLayoutSubviews() {
let orientation = UIDevice.current.orientation
if orientation == .portrait
{
landscapeHeight.isActive = false
portraitHeight.isActive = true
}else if orientation == .landscapeLeft || orientation == .landscapeRight
{
portraitHeight.isActive = false
landscapeHeight.isActive = true
}
}
}
But what I really want is to be able to squish the views when there is not enough space. I tried changing UILayoutPriority but it didn't work.
These are my constraints:
NSLayoutConstraint.activate([
firstView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
firstView.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor),
firstView.rightAnchor.constraint(equalTo: view.safeAreaLayoutGuide.rightAnchor),
firstView.heightAnchor.constraint(equalToConstant: 88),
secondView.topAnchor.constraint(equalTo: firstView.bottomAnchor),
secondView.leftAnchor.constraint(equalTo: firstView.leftAnchor),
secondView.rightAnchor.constraint(equalTo: firstView.rightAnchor),
secondView.heightAnchor.constraint(equalTo: firstView.heightAnchor),
thirdView.topAnchor.constraint(equalTo: secondView.bottomAnchor),
thirdView.leftAnchor.constraint(equalTo: firstView.leftAnchor),
thirdView.rightAnchor.constraint(equalTo: firstView.rightAnchor),
thirdView.heightAnchor.constraint(equalTo: firstView.heightAnchor),
fourthView.topAnchor.constraint(equalTo: thirdView.bottomAnchor),
fourthView.leftAnchor.constraint(equalTo: firstView.leftAnchor),
fourthView.rightAnchor.constraint(equalTo: firstView.rightAnchor),
fourthView.heightAnchor.constraint(equalTo: firstView.heightAnchor),
fifthView.topAnchor.constraint(equalTo: fourthView.bottomAnchor),
fifthView.leftAnchor.constraint(equalTo: firstView.leftAnchor),
fifthView.rightAnchor.constraint(equalTo: firstView.rightAnchor),
fifthView.heightAnchor.constraint(equalTo: firstView.heightAnchor),
])
CodePudding user response:
The key here is indeed to make some constraints have less priority. Namely, for each view, the constraint "height=88" should have a non-required priority, so that it can be broken when the screen height is not enough. On the other hand, you should add another "height <= 88" required constraint.
let view1 = UIView()
view1.backgroundColor = .yellow
let view2 = UIView()
view2.backgroundColor = .blue
let view3 = UIView()
view3.backgroundColor = .brown
let view4 = UIView()
view4.backgroundColor = .cyan
let view5 = UIView()
view5.backgroundColor = .green
let views = [view1, view2, view3, view4, view5]
for view in views {
view.translatesAutoresizingMaskIntoConstraints = false
let heightConstraint = view.heightAnchor.constraint(equalToConstant: 88)
heightConstraint.priority = .defaultHigh
heightConstraint.isActive = true
view.heightAnchor.constraint(greaterThanOrEqualToConstant: 88).isActive = true
}
Rather than adding lots of constraints relating the 5 views, I strongly recommend that you use a stack view for this.
let stackView = UIStackView(arrangedSubviews: views)
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
Since you want the views to all be positioned at the top when there is enough space, you should add these constraints relating the stack view and its super view:
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor),
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor),
])
Note especially the last one - it's another "<=" constraint. This is what prevents the stack view from exceeding the screen's frame in landscape.
Output: