有多种方法可以解决这个问题。
一种方法是使用两个“底部”约束:
- 从按钮底部到
detailView底部的一个
- 从
detailText 底部到detailView 底部的一个
然后根据视图是“折叠”还是“展开”来设置每个的约束优先级。
这是一个完整的实现供您尝试:
class ExpandViewController: UIViewController {
let myButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setTitle("Collapse", for: [])
v.backgroundColor = .red
return v
}()
let detailText: UITextView = {
let v = UITextView()
v.translatesAutoresizingMaskIntoConstraints = false
v.text = "This is text in the text view."
return v
}()
let detailView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = UIColor(red: 0.25, green: 0.5, blue: 1.0, alpha: 1.0)
v.clipsToBounds = true
return v
}()
var isAnimating: Bool = false
var collapsedConstraint: NSLayoutConstraint!
var expandedConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 0.0, green: 0.75, blue: 0.0, alpha: 1.0)
detailView.addSubview(myButton)
detailView.addSubview(detailText)
view.addSubview(detailView)
let g = view.safeAreaLayoutGuide
// when collapsed, we want button bottom to constrain detailView bottom
collapsedConstraint = myButton.bottomAnchor.constraint(equalTo: detailView.bottomAnchor, constant: -12.0)
// when expanded, we want textView bottom to constrain detailView bottom
expandedConstraint = detailText.bottomAnchor.constraint(equalTo: detailView.bottomAnchor, constant: -12.0)
// we'll start in Expanded state
expandedConstraint.priority = .defaultHigh
collapsedConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
// constrain detailView Top / Leading / Trailing
detailView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
detailView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
detailView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// no Height or Bottom constraint for detailView
// constrain button Top / Center / Width
myButton.topAnchor.constraint(equalTo: detailView.topAnchor, constant: 12.0),
myButton.centerXAnchor.constraint(equalTo: detailView.centerXAnchor),
myButton.widthAnchor.constraint(equalToConstant: 200.0),
// constrain detailText Top / Leading / Trailing
detailText.topAnchor.constraint(equalTo: myButton.bottomAnchor, constant: 12.0),
detailText.leadingAnchor.constraint(equalTo: detailView.leadingAnchor, constant: 12.0),
detailText.trailingAnchor.constraint(equalTo: detailView.trailingAnchor, constant: -12.0),
// constrain detailText's Height
detailText.heightAnchor.constraint(equalToConstant: 200.0),
expandedConstraint,
collapsedConstraint,
])
myButton.addTarget(self, action: #selector(self.expandViewPressed(sender:)), for: .touchUpInside)
}
@objc func expandViewPressed(sender: UIButton) {
if isAnimating { return }
animateView()
}
private func animateView() {
isAnimating = true
// if it's expanded
if expandedConstraint.priority == .defaultHigh {
expandedConstraint.priority = .defaultLow
collapsedConstraint.priority = .defaultHigh
} else {
collapsedConstraint.priority = .defaultLow
expandedConstraint.priority = .defaultHigh
detailText.isHidden = false
}
UIView.animate(withDuration: 1, animations: {
self.view.layoutIfNeeded()
}) { (_) in
self.detailText.isHidden = self.expandedConstraint.priority == .defaultLow
self.isAnimating = false
self.myButton.setTitle(self.detailText.isHidden ? "Expand" : "Collapse", for: [])
}
}
}