Swift do I need layoutifneeded() - ios

I am animating a view by:
#IBAction func showInfo(sender: AnyObject) {
UIView.animateWithDuration(1,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: nil,
animations: {
self.infoContainer.frame.origin.y = CGFloat(30)
}, completion: nil
)
}
My question now is if I need to run layoutIfNeeded() afterwards?

You need to call layoutIfNeeded() when you change constraints. You don't need to call it when animating by origin

Since you are animating by setting a new frame, you don't need to call layoutIfNeeded().
My guess is that you asked this question because it's not working for you. If you setup the view with AutoLayout, and try to modify the frame directly, it won't work, you have to animate by modifying the constants in the constraints.

Related

UIView.animate called in didMoveToSuperview unexpectedly affects all subviews

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 ?
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.
why...?
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.
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.
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)

Animate constraints and frame at the same time

I want to override the perform method of an UIStoryboardSegue subclass.
Therefore I will have to animate constraints and frames of different objects using UIView.animateWithDuration.
Is it possible, to do such animations of constraints and frames in one UIView.animateWithDuration method?
Can you post an example?
Use layoutIfNeeded() to animate constraint.
view.layoutIfNeeded()
constraintToAnimate.constant = 0
UIView.animate(withDuration: 1.0, animations: { () -> Void in
self.view.layoutIfNeeded()
}

Can all constraints in swift 3 be animated?

I want to animate a view like using the following code:
UIView.animate(withDuration: 2.7, delay: 0.1, options: .allowAnimatedContent, animations: {
self.AVCenterY.constant = 0.8
}, completion: nil)
But it happens so fast it seems like it is not animated. On the other hand, when I animate the property alpha it is animated (it takes the 2.7 seconds to change). I used 2.7 sec to make sure the problem was that I was using a small duration time.
Constraints cannot be animated at all. It is the act of layout that can be animated:
UIView.animate(withDuration: 2.7, delay: 0.1, options: .allowAnimatedContent, animations: {
self.AVCenterY.constant = 0.8
theView.superview?.layoutIfNeeded() // *
}, completion: nil)
When we animate the act of layout (or when the runtime does so), then any constraint changes are also automatically animated.
Note that what I animate is the layout of the superview of the view that is to move. I called it theView but that is just something I made up. You will need an outlet to that view so that you can get its superview, and use the name of that outlet.

Animating collection view height change (Swift)

I am trying to animate the height change for a collection view, but I can't figure out how to get the animation to work. The height changes correctly, but it happens immediately, not animated.
#IBOutlet weak var collectionViewHeight: NSLayoutConstraint!
UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseOut, animations: {
self.collectionViewHeight.constant = 0
}
I tried searching for solutions, but couldn't find anything specific to this situation. Also tried layoutIfNeeded() suggested here, but it didn't help: Animate view height with Swift
Any help much appreciated!
You should update the constant of the constraint outside of the animation block:
self.collectionViewHeight.constant = 0
UIView.animateWithDuration(0.3, delay: 0, options: .CurveEaseOut,
animations: view.layoutIfNeeded, completion: nil)
(view is the superview of the collection view in question.)

swift animateWithDuration animation not right

I am hiding my navigation bar and a UIView under it that acts as a extension bar to it when I scroll my page.
My app is built like:
VC that holds a container view with an embedded table view.
From the table view I have delegates that notify VC1 once a user scrolls up or down.
My problem now is that the animation dont looks that good. What I am trying to do is to animate the extension bar to animate up or down with a fade in or fade out effect as well. When that occurs I also update the top contraint on my container view so that the table view will fill the whole screen. (I am not sure if I use layoutneeded() right or if something else should be used when updating constraints)
My code:
func ContainerTableViewControllerScrolledUp(controller: ContainerTableViewController) {
self.navigationController?.setNavigationBarHidden(false, animated: true)
println("UP")
UIView.animateWithDuration(
1.5,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: nil,
animations: {
self.extensionV.alpha = 1
self.tableVConst.constant = 0
}, completion: { finished in
self.view.layoutIfNeeded()
}
)
}
func ContainerTableViewControllerScrolledDown(controller:ContainerTableViewController) {
self.navigationController?.setNavigationBarHidden(true, animated: true)
println("DOWN")
UIView.animateWithDuration(
1.5,
delay: 0,
usingSpringWithDamping: 0.7,
initialSpringVelocity: 0.5,
options: nil,
animations: {
self.extensionV.frame.origin.y = CGFloat(-10)
self.tableVConst.constant = -41
self.extensionV.alpha = 0
}, completion: { finished in
self.view.layoutIfNeeded()
}
)
}
extensionV is the extension view
tableVConst is the top constraint for my container view that holds my table view
So how should I edit my code in order to get the extension view to animate up/down with a fade in/fade out effect?
Instead of calling self.view.layoutIfNeeded() in the completion block, try calling it inside the animation block on the last line before it returns.

Resources