Reputation: 4023
I want the second label (subview2
) to stay intact and have the first label (subview1
) shrink as the size of the container view decreases in size, but the second label is still shrink no matter how I adjust the priority:
import UIKit
import PlaygroundSupport
let containerView = UIView(frame: CGRect(origin: .zero, size: .init(width: 200, height: 300)))
let subview1 = UILabel()
subview1.text = "Hello"
subview1.backgroundColor = .red
containerView.addSubview(subview1)
subview1.translatesAutoresizingMaskIntoConstraints = false
let subview2 = UILabel()
subview2.text = "Hello"
subview2.backgroundColor = .cyan
containerView.addSubview(subview2)
subview2.translatesAutoresizingMaskIntoConstraints = false
let views: [String: Any] = ["subview1": subview1, "subview2": subview2]
let con1 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview1]", metrics: nil, views: views)
let con2 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview2]", metrics: nil, views: views)
let con3 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(30)-[subview1]", metrics: nil, views: views)
let con4 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview2]-(30)-|", metrics: nil, views: views)
let con5 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview1(>=100)]-(>=30)-[subview2(>=100)]", metrics: nil, views: views)
NSLayoutConstraint.activate(con1 + con2 + con3 + con4 + con5)
subview1.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: .horizontal)
subview2.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = containerView
I've tried increasing the priority of subview2
manually, but still didn't work:
let priority1 = subview2.contentCompressionResistancePriority(for: .horizontal)
print(priority1) // 750
let priority2 = subview2.contentCompressionResistancePriority(for: .horizontal)
print(priority2) // 750
subview2.setContentCompressionResistancePriority(priority2 + 100, for: .horizontal)
Upvotes: 0
Views: 1557
Reputation: 77442
VFL (Visual Format Language) is very out-dated and limited. I strongly suggest you switch to more modern (and more flexible) syntax.
That said, the problem is with this line:
let con5 = NSLayoutConstraint.constraints(withVisualFormat:
"H:[subview1(>=100)]-(>=30)-[subview2(>=100)]", metrics: nil, views: views)
Your code is saying:
subview1
width must be greater-than-or-equal-to 100
subview2
width must be greater-than-or-equal-to 100
so, the setContentCompressionResistancePriority
lines do nothing. You've already said "these are the required minimum widths."
If you change that line to:
let con5 = NSLayoutConstraint.constraints(withVisualFormat:
"H:[subview1]-(>=30)-[subview2]", metrics: nil, views: views)
you should get your desired result.
Here, though, is your code using more modern constraint syntax -- you may find it much more logical and easier to understand:
NSLayoutConstraint.activate([
// both labels 20-pts from containerView Top
subview1.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
subview2.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 20.0),
// left label Leading 30-pts from containerView Leading
subview1.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 30.0),
// right label Trailing 30-pts from containerView Trailing
subview2.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -30.0),
// right label Leading at least 30-pts from left label Trailing
subview2.leadingAnchor.constraint(greaterThanOrEqualTo: subview1.trailingAnchor, constant: 30.0),
])
// don't do any of this...
//let views: [String: Any] = ["subview1": subview1, "subview2": subview2]
//let con1 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview1]", metrics: nil, views: views)
//let con2 = NSLayoutConstraint.constraints(withVisualFormat: "V:|-(20)-[subview2]", metrics: nil, views: views)
//let con3 = NSLayoutConstraint.constraints(withVisualFormat: "H:|-(30)-[subview1]", metrics: nil, views: views)
//let con4 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview2]-(30)-|", metrics: nil, views: views)
//let con5 = NSLayoutConstraint.constraints(withVisualFormat: "H:[subview1]-(>=30)-[subview2]", metrics: nil, views: views)
//NSLayoutConstraint.activate(con1 + con2 + con3 + con4 + con5)
Edit - to answer comment...
If you want the widths of your labels to each be >= 100
, but allow them to compress (left-label compresses first), you need to add width constraints with Priority of less-than .required
:
// both labels should have a width of >= 100
let leftWidth = subview1.widthAnchor.constraint(greaterThanOrEqualToConstant: 100.0)
let rightWidth = subview2.widthAnchor.constraint(greaterThanOrEqualToConstant: 100.0)
// set the Priority on the width constraints to less than required
// to allow auto-layout to break that constraint if needed
leftWidth.priority = .defaultHigh
rightWidth.priority = .defaultHigh
// activate the width constraints
leftWidth.isActive = true
rightWidth.isActive = true
// tell leftLabel to compress before rightLabel
subview1.setContentCompressionResistancePriority(UILayoutPriority.defaultLow, for: .horizontal)
subview2.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)
Upvotes: 1