Reputation: 1436
I'm working on a custom UIView. I got it all working properly, but my design requirements dictate that parts of the view should animate upon load.
My view is set in the following way, and I chose to animate constraints:
So I called UIView.animate()
in didMoveToSuperview()
like such:
override func didMoveToSuperview() {
animateArrow()
}
private func animateArrow() {
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat], animations: {
self.arrowLeadingConstraint.constant += 15
self.layoutIfNeeded()
}, completion: nil)
}
I'm not doing anything else. On its own, the animation only affects the leading constraint of my arrow image view. As expected, and as it should. I can verify this when I start the animation upon user interaction, as pictured below.
Now, the problem is, when called from within didMoveToSuperview()
, the animation somehow affects all subviews of my custom UIView...
What am I doing wrong ?
Upvotes: 0
Views: 711
Reputation: 698
Call the animation code here:
override func draw(_ rect: CGRect) {
super.draw(rect)
animateArrow()
}
Or
as suggested by @holex comment: perform a layout pass before:
private func animateArrow() {
self.layoutIfNeeded();
self.arrowLeadingConstraint.constant = 31;
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat]) {
self.layoutIfNeeded()
}
}
Also add an observer in your init
if animation stops on pressing home:
NotificationCenter.default.addObserver(self, selector: #selector(enteredForeground(_:)), name: .UIApplicationWillEnterForeground, object: nil)
Upvotes: 1
Reputation: 24041
it seems the devil is in your animateArrow()
method itself, if you amend the method a little bit like e.g. this:
private func animateArrow() {
self.layoutIfNeeded()
self.arrowLeadingConstraint.constant = 31 // = 15 + 16 from your original code
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseOut, .autoreverse, .repeat], animations: {
self.layoutIfNeeded()
}, completion: nil)
}
tada, the animation will work properly as you expected.
my explanation may not be academic here but I hope it will make sense to the readers for getting a better understanding.
so, briefly, when you are dealing with constraints you are implicitly dealing with a set of predefined relationships between the view and its surroundings. that is why you cannot animate an individual constraint successfully (your original attempt) because only these relationships are animatable in this context – not the constraints.
therefore you will be able to animate the update of all relationships only after you defined the new constraint(s) for your layout – and in principle behind the scenes that could lead to animate every affected view's frame for you in one go.
you can read more about what the constraints are and how the evaluation works with Auto-Layout from Apple, if you are interested in that.
Upvotes: 1
Reputation: 2371
Using didMoveToSuperview
might not be the best idea. Before starting the animation you need to make sure that the layout for all the views on the screen has been done, which might not always be true in didMoveToSuperview
.
I would move the animation trigger inside viewDidAppear
in the viewController or in didLayoutSubviews
which is also in the viewcontroller.
Upvotes: 1