I have created a popup in swift using UIView and I want to bounce it when displaying, but I don't know how to do that. I want to make the same effect as seen below:
You should use UIView.animate with usingSpringWithDamping and initialSpringVelocity parameters.
Eg:
// Change position of view
UIView.animate(withDuration: 1.0, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: [], animations: {
// Call layoutIfNeeded()
})
Or you can change view's position in animation completion block. But it's not recommended.
Related
I've decided to switch to constraints and face up with animation problems. In autoresizing mask world everything works fine. UIView is attached to right top. Content inside use autoresizing mask.
Animate code:
UIView.animate(withDuration: 0.3, delay: 0, options: [.curveEaseInOut]) {
self.constraints.first(where: {$0.firstAttribute == .height})?.constant = dstSize.height
self.constraints.first(where: {$0.firstAttribute == .width})?.constant = dstSize.width
self.setNeedsLayout()
self.layoutIfNeeded()
}
You need to move constraint's change outside the animation block
self.constraints.first(where: {$0.firstAttribute == .height})?.constant = dstSize.height
self.constraints.first(where: {$0.firstAttribute == .width})?.constant = dstSize.width
UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseInOut]) {
self.superView!.layoutIfNeeded()
}
Update
In your Github attached code you need to re-layout the main view not the container
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut) {
self.view.layoutIfNeeded()
}
A few things:
It's good practice to make properties for the constraints you want to update later;
Only the layoutIfNeeded call have to be inside an animation block;
It looks like setNeedsLayout is unnecessary. Have you tried to remove it?
I have a VideoView (it is a child view of UIView).
By default, it is added to a UIView which is small view in the corner of the screen (I called it ParrentView1).
I have a button to zoom out VideoView. This button performs an action that removes VideoView from ParentView1 and adds it to a bigger view (called ParrentView2).
When I perform the code below, it works but the animation is weird. All I need is a zoom out animation from ParrentView1 to ParrentView2.
Here is my code:
VideoView.removeFromSuperview()
ParrentView2.addSubview(VideoView)
UIView.animate(withDuration: 0.8, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
VideoView.frame = ParrentView2.bounds
}, completion: nil)
thanks for helping
The likely cause is that when adding it to the other view, it gets assigned a different frame. The solution is to make sure the animation starts at the original location. Something like:
CGRect originalRect = ParrentView2.convert(VideoView.frame, from:VideoView.superView);
VideoView.removeFromSuperview()
ParrentView2.addSubview(VideoView)
VideoView.frame = originalRect;
UIView.animate(withDuration: 0.8, delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 1,
options: .curveEaseOut,
animations: {
VideoView.frame = ParrentView2.bounds
}, completion: nil)
An improvement point: note that it is customary in Swift to start variable names with a lower case letter. It gets confusing when they don't.
I'm struggling to understand why the following key-framed animation doesn't perform as I expect. Here minuteClock is a subclassed UIView that is also a child view of the main view. minuteClock's horizontal position is set via an exposed constraint. minuteClock's own layer has a CAShapeLayer sublayer called borderLayer.
I'm trying to simultaneously animate the location of minuteClock and the fillcolor of that grandchild borderLayer. What I'm finding is that the keyframes work for the positioning but are ignored for the CAShapeLayer. That is, while the minuteClock moves to 100 half-way through the overall duration, I don't see borderLayer's fillcolor turn red. Instead it seems to just animates very quickly (like a quarter-second) to green.
UIView.animateKeyframes(withDuration: 2.0,
delay: 0,
options: .calculationModeLinear,
animations: {
UIView.addKeyframe(withRelativeStartTime: 0.0,
relativeDuration: 0.5,
animations: {
self.minuteClockPositionConstraint.constant = 100
self.minuteClock.borderLayer.fillColor = UIColor.red.cgColor
self.view.layoutIfNeeded()
})
UIView.addKeyframe(withRelativeStartTime: 0.5,
relativeDuration: 0.5,
animations: {
self.minuteClockPositionConstraint.constant = 50
self.minuteClock.borderLayer.fillColor = UIColor.green.cgColor
self.view.layoutIfNeeded()
})
}, completion: nil)
I think I'm probably going about this wrong, mixing up keyframed animation at the view level with implicit animation triggered by changing a property on a CAShapeLayer.
Any suggestions as to how I should be doing this?
I have seen here very smooth animation of hiding/showing subview in UIStackView.
I try to reproduce it in my own application but I have meet a problem.
View that is hidden during animation proces does not resize. It just wait until animation finish and then disappear. Opposite to clear button from above linked example.
My code:
UIView.animate(withDuration: 0.5,
delay: 0.0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: [],
animations: {
self.acceptDeclineBar.isHidden = !newState
self.view.layoutIfNeeded()
},
completion: nil)
Question
Is UIStackView give resizing animation on hide/show for free or do I need implement it for myself using height constraint for example?
Your view (self.acceptDeclineBar) will hide when the animation completes. try to hide before the animation.
self.acceptDeclineBar.isHidden = !newState
UIView.animate(withDuration: 0.3){ [weak self]
self?.view.layoutIfNeeded()
}
or instead of hiding you can use Height Constraint
acceptDeclineBarHeightConstraint.constant = newState ? 60 (whatever Visbale size) : 0 (Hide)
UIView.animate(withDuration: 0.3){ [weak self]
self?.view.layoutIfNeeded()
}
When I animate a change to a view's transform, then reset that change in another animation before the first animation finishes, everything's great (shown here with a rotation). The animation smoothly switches to the new target:
But when I do this with a scale, the animation overshoots magnificently:
Here's the breaking code:
UIView.animateWithDuration(1) {
self.someView.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1)
}
UIView.animateWithDuration(1,
delay: 0.5,
options: nil,
animations: {
self.someView.layer.transform = CATransform3DIdentity
}, completion: nil
)
Has anyone else seen this? Am I doing something wrong?
EDIT: And is there a good workaround?
EDIT 2: I believe this is a duplicate of this question and am voting to close.
This blog post provides the answer: in iOS 8, UIView animations are additive, and this has an unfortunate result with scale animations.
Basically, the second animation happens together with the first animation. The only solution is to explicitly remove the original animation before starting a new one:
view.layer.transform = view.layer.presentationLayer().transform
view.layer.removeAllAnimations()
Hi I'm not quite sure what your looking for but if you want the view to go back to it's original scale you'd add the .Autoreverse flag.
UIView.animateWithDuration(1, delay: 0, options: .Autoreverse | .Repeat, animations: {
myView.layer.transform = CATransform3DMakeScale(0.001, 0.001, 1)
}, completion: nil)
While if you wanted to string animations together I'd do it within UIView.animateKeyframesWithDuration()
UIView.animateKeyframesWithDuration(2, delay: 0.0, options: nil, animations: {
UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5, animations: {
// Animation 1
})
UIView.addKeyframeWithRelativeStartTime(1, relativeDuration: 0.5, animations: {
// Animation 2
})
}, completion: nil)