Reputation: 235
I have created a button which is attached to the top, leading and trailing and has fixed height. And when I'm tapping the button the new UIView instance should appear with animation.
import UIKit
class ViewController: UIViewController {
var topButton = UIButton()
var myView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
topButton = UIButton(type: .roundedRect)
topButton.setTitle("Add Acctivity", for: .normal)
topButton.backgroundColor = UIColor.purple
topButton.setTitleColor(UIColor.white, for: .normal)
topButton.titleLabel?.font = UIFont(name: "System", size: 26)
topButton.translatesAutoresizingMaskIntoConstraints = false
topButton.addTarget(self, action: #selector(expandMenu), for: .touchUpInside)
view.addSubview(topButton)
topButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
topButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
topButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
topButton.heightAnchor.constraint(equalToConstant: 70).isActive = true
myView.backgroundColor = UIColor.blue
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 70).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
@objc func expandMenu() {
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}
}
}
And the interesting thing is when I'm putting the height smaller than its initial value the animation works.
I saw many tutorials when people are creating IBOutlet from storyboard and then changing its constant value. But I'm wondering how to do that programmatically.
Upvotes: 2
Views: 5448
Reputation: 31645
The issue in your code is that you are trying to reactivate another constraint that describes the height of myView
, by saying (in expandMenu()
):
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
I would assume that you are getting a runtime warning similar to:
[LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. ( "NSLayoutConstraint:0x60400008e5b0 UIView:0x7fa16a419450.height == 100 (active)>", "NSLayoutConstraint:0x608000097e30 UIView:0x7fa16a419450.height == 300 (active)>" )
Will attempt to recover by breaking constraint
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in may also be helpful.
means that myView
already has a constraint for height, which is (in viewDidLoad()
):
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
What you should do instead is to change the constant
value of the same constraint instead. You could achieve it like this:
Declare a NSLayoutConstraint
as instance variable:
var myViewHeightConstraint: NSLayoutConstraint!
and in viewDidLoad()
:
myViewHeightConstraint = NSLayoutConstraint(item: myView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0.0, constant: 100)
myViewHeightConstraint.isActive = true
instead of:
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
Finally, in expandMenu()
:
myViewHeightConstraint.constant = 300
instead of:
myView.heightAnchor.constraint(equalToConstant: 300).isActive = true
Output:
Furthermore:
If you are aiming to let the same button to achieve the expand/collapse behavior, you would need to simply implement it as (in expandMenu()
):
myViewHeightConstraint.constant = myViewHeightConstraint.constant == 300 ? 100 : 300
Upvotes: 7
Reputation: 271040
The constraint
method returns an NSLayoutConstraint
. You can assign it to a class level variable:
var heightConstraint: NSLayoutConstraint!
override func viewDidLoad() {
// ...
heightConstraint = myView.heightAnchor.constraint(equalToConstant: 100)
heightConstraint.isActive = true
}
And then you will have a property heightConstraint
that is very much like an IBOutlet
. You can animate this the same way you animate an IBOutlet
constraint.
Upvotes: 1