Rotation animation with spring effect - ios

I want to make rotation of CAShapeLayer with spring effect (like in UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:)) but on layer not on a view.
When the button is tapped its sublayer of main layer should rotate to 3*PI/4 and spring should bounce to 2*PI/3. Then, when button is tapped again, layer rotation should be done in reversed order than before: first bounce to 2*PI/3, then rotation to the initial position (before first rotation).
How I could do that? I cannot achieve it by UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:) because layer's transform property is animatable by default.
I've tried changing CATransaction but it rotates only by one angle (without taking into consideration other rotation):
let rotation1 = CGAffineTransformRotate(CGAffineTransformIdentity, angle1)
let rotation2 = CGAffineTransformRotate(CGAffineTransformIdentity, angle2)
let transform = CGAffineTransformConcat(rotation1, rotation2)
CATransaction.begin()
CATransaction.setAnimationDuration(0.6)
self.plusLayer.setAffineTransform(transform)
CATransaction.commit()
Update
According to Duncan C post I try to use CASpringAnimation and I achive animation in one direction:
myLayer.setAffineTransform(CGAffineTransformMakeRotation(angle))
let spring = CASpringAnimation(keyPath: "transform.rotation")
spring.damping = 12.0
spring.fromValue = 2.0 * CGFloat(M_PI)
spring.toValue = 3.0 * CGFloat(M_PI_4)
spring.duration = 0.5
myLayer.addAnimation(spring, forKey: "rotation")
But how to reverse that animation on button tapped?
Thanks in advance for your help.

UIView block animation is for animating view properties. You could animate the button's transform (of type CGAffineTransform, a 2D transform) using UIView.animateWithDuration(_:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
If you need to animate layer properties, though, you'll need to use Core Animation.
It seems Apple added (or made public) a spring CAAnimation in iOS 9. It doesn't seem to be in the Xcode docs however.
Check out this thread:
SpringWithDamping for CALayer animations?

Related

CABasicAnimation on UIView, Child button is not clickable

I have view which is rotating like this :
if view.layer.animation(forKey: kRotationAnimationKey) == nil {
let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotationAnimation.fromValue = 0.0
rotationAnimation.toValue = Float(M_PI * 2.0)
rotationAnimation.duration = duration
rotationAnimation.repeatCount = Float.infinity
view.layer.add(rotationAnimation, forKey: kRotationAnimationKey)
}
there are multiple small button on this view.
While parent view is rotating and trying to tap on buttons , i am unable to tap and corresponding action method is not getting called.
Please help on this issue.
When you animate a view or layer, the actual view does not move through the intermediate locations of the animation. Instead, the view object jumps to it's final location at the beginning of the animation. (The animation actually takes place on a "presentation layer" which is laid on top of the view's normal layer hierarchy.)
If you want to be able to tap on a button that's "in flight" in an animation you will have to do hit testing on the view's presentation layer, and you can't have a button respond to touches using target/action.
I have a sample project on Github that demonstrates detecting taps on an in-flight view if you're interested. Take a look at https://github.com/DuncanMC/iOS-CAAnimation-group-demo on Github. It's written in Objective-C, but the idea is the same

How to move a buttons target/gesture recogniser when animating the button in swift (iOS)? [duplicate]

This question already has answers here:
UIButton not interacting during animation
(5 answers)
Closed 6 years ago.
Im animating some buttons in swift to move randomly across the view forever, however the buttons 'target' does not move. To click the button, you have to click the original position that it is created at, even though it might be the other side of the screen.
Does anyone have simple instructions for how to move the buttons target with the button?
func circlePath(view: UIButton, pathDuration: Double){
//create an animation to follow a circular path
let pathAnimation: CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "position")
//interpolate the movement to be more smooth
pathAnimation.calculationMode = kCAAnimationPaced
pathAnimation.repeatCount = .infinity
//no ease in/out to have the same speed along the path
pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
//the paths circular animation is proportional its size
pathAnimation.duration = pathDuration
//The circle to follow will be inside the circleContainer frame.
//it should be a frame around the center of your view to animate.
//do not make it to large, a width/height of 3-4 will be enough.
let curvedPath: CGMutablePathRef = CGPathCreateMutable()
let circleContainer: CGRect = CGRectInset(view.frame, 23, 23)
CGPathAddEllipseInRect(curvedPath, nil, circleContainer)
//add the path to the animation
pathAnimation.path = curvedPath
//add animation to the view's layer
view.layer.addAnimation(pathAnimation, forKey: "myCircleAnimation")
}
Don't use the term "target" to mean the area that taps respond to. "Target" has a specific meaning with regard to buttons, and it's not that.
Let's call it the "hit rectangle".
Core Animation does not actually animate the objects across the screen. It creates a "presentation layer" that gives the appearance of the objects moving, but the objects themselves do not animate.
Thus you can't have buttons respond to taps while "mid-flight" in an animation.
Instead, what you have to do is to put a tap gesture recognizer on the superview (or the layer that contains the layer you're animating, if you're doing CAAnimation.) Then you can use the hitTest method of the animated layer's presentation layer to test if the tap was inside the bounds of the layer that's being animated.
I have a project on github that show this technique. It's called iOS-CAAnimation-group-demo (link.) It's written in Objective-C, but the technique is the same regardless of language so it should give you an idea of how to proceed.

Rotate CAShapeLayer around it center without moving postion

I want to rotate a CAShapeLayer with objective c around it center point without moving it around. The CAShapeLayer contain UIBezierPath point of rect. I'm not able to rotate the CAShapeLayer becouse i dont know how. Please show me how tp rotate around it center without moving it postion.
Here is some code that does that:
//Create a CABasicAnimation object to manage our rotation.
CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:#"transform"];
totalAnimationTime = rotation_count;
rotation.duration = totalAnimationTime;
//Start the animation at the previous value of angle
rotation.fromValue = #(angle);
//Add change (which will be a change of +/- 2pi*rotation_count
angle += change;
//Set the ending value of the rotation to the new angle.
rotation.toValue = #(angle);
//Have the rotation use linear timing.
rotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
/*
This is the magic bit. We add a CAValueFunction that tells the CAAnimation we are modifying
the transform's rotation around the Z axis.
Without this, we would supply a transform as the fromValue and toValue, and for rotations
> a half-turn, we could not control the rotation direction.
By using a value function, we can specify arbitrary rotation amounts and directions, and even
Rotations greater than 360 degrees.
*/
rotation.valueFunction = [CAValueFunction functionWithName: kCAValueFunctionRotateZ];
/*
Set the layer's transform to it's final state before submitting the animation, so it is in it's
final state once the animation completes.
*/
imageViewToAnimate.layer.transform = CATransform3DRotate(imageViewToAnimate.layer.transform, angle, 0, 0, 1.0);
//Now actually add the animation to the layer.
[imageViewToAnimate.layer addAnimation:rotation forKey:#"transform.rotation.z"];
(That code is taken (and simplified) from my github project KeyframeViewAnimations)
In my project I'm rotating the layer of a UIImageView, but the same approach will work for any CALayer type.

CABasicAnimation change startingpoint to go forward and backwards

I'm trying to make a small ui controlled animation (as described in this answer) and continue this animation after rotation. Due to autolayout issues I have to remove and add the whole layer on autorotate.
This works so far. My problem is, as I want continue the animation from the same position it was left off, it won't go anywhere prior to the state it was the moment it rotated.
e.g. the slider is at 0.5. The animation was added again (due to removal) and I've set the timeOffset corresponding to 0.5. The animation will go forward, but not backward.
I create my animation via:
let animatePhase = CABasicAnimation(keyPath: "lineDashPhase")
animatePhase.byValue = phaseLength
animatePhase.duration = 1.0
animatePhase.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animatePhase.repeatCount = Float.infinity
lineLayer.addAnimation(animatePhase, forKey: "marching ants")
lineLayer.speed = 0.0
lineLayer.timeOffset = 0.0;
lineLayer.beginTime = 0.0;
lineLayer is a CAShapeLayer.
On `layoutSubviews' I remove and recreate the layer.
What am I doing wrong?
Thanks

iOS, Swift: animation controlled by gesture

I trying to find a way for connecting user interaction (pan gesture) with animation. I want to control animation progress by dragging view (or just by finger panning on the screen).
I think I can make this by using Core Animation, but I didn't find any similar to my needs example.
I have a CGPath, that I want to draw.
I know that there are strokeStart and strokeEnd property that is animatable and perfectly acceptable for my needs in the "drawing" point of view.
Using layer with given path property I can animate this path with CABasicAnimation
layer.path = myPath
//... layer configuration
let pathAnimation = CABasicAnimation(keyPath: "strokeEnd")
pathAnimation.duration = 2
pathAnimation.fromValue = 0
pathAnimation.toValue = 1
layer.addAnimation(pathAnimation, forKey: "strokeEndAnimation")
Here we can see animation from the first path point to the last consequentially.
Okay, but I need to somehow control this animation with gesture.
Let's say, if we pan finger on the screen to the right, our path animation draws straight ahead (e.g strokeStart = 0 and strokeEnd = 1) and if we pan from right to left we will see backward animation (like rewind) (e.g. strokeStart = 1 and strokeEnd = 0).
I know, how to get gesture direction, translationInView, velocityInView and I want to animate this in changed state, not end. (UIGestureRecognizerState.Changed)
But I don't know how I can combine control from gesture to animation because of this troubles:
duration of animation. (I don't know how fast or how slow gesture would be)
how I need to implement animation because calls are very frequent in changed state
I looked to
animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:
but duration question rises again.
Hope somebody can help me, ask me if you misunderstood something and I'll try to clarify.
Let's say, if we pan finger on the screen to the right, our path animation draws straight ahead (e.g strokeStart = 0 and strokeEnd = 1) and if we pan from right to left we will see backward animation (like rewind) (e.g. strokeStart = 1 and strokeEnd = 0).
If I understand you correctly, the solution is to freeze the animation by setting the layer's speed to 0 and then setting the "frame" of the animation by setting the layer's timeOffset.

Resources