CAAnimation repeatable animation queue - ios

Is there is any way to create repeatable animation queue using CAAnimation? For example: I have a ball which should make one clockwise rotation (2 * PI) and two counterclockwise rotations (4*PI) and these animations should repeated forever.

You could either use a UIView keyframe animation (using animateKeyframesWithDuration) set to repeat, or create a CAAnimationGroup composed of your separate animation steps timed to run one right after the other, and set up the entire animation group to repeat.
Getting rotations of a full circle or more is a little tricky however. Normally a rotation of 2*PI doesn't do anything because the end result is that the layer winds up at the same angle.
The secret is to use a CAValueFunction to specify a rotation around the Z axis (assuming that's what you want.)
I have a project called KeyframeViewAnimations that illustrates both creating keyframe animations and using CAValueFunction to do rotations of > 2PI (although it's two separate animations in my sample project.)

Related

How do I animate a caret shape into a smooth arc?

I want to create an animation where a caret shape (">") animates into a circle arc. It's for an application where there is a caret that transforms into a 3/4 circular arc, which then spins while a network transaction runs.
The animation should look like this:
Any time I try to do path animations like this, I get very strange results. How do I create a smooth animation?
The secret to this sort of animation is to install a path into a CAShapeLayer and use a CABasicAnimation on the path that transforms the path from its starting state to it's end state. The tricky part is that the starting and ending paths need to have the same number and type of control points.
I wrote a demo app that creates exactly the animation above. You can download it from Github (link)
Here is the readme from the repo, which explains how it works in some detail:
CaretToArcAnimation
This project animates a simple transform of a caret symbol to an arc that spans 3/4 of a circle. It looks like this:
It works by animating the path installed into a CAShapeLayer.
In order for a path animation to work correctly, the starting and ending paths need to have the same number and type of control points.
To animate from the caret to the arc, it creates the caret as 2 cubic bezier curves. The first "curve" (Which is actually a straight line) starts at the lower left of the caret, passes through points 1/3 and 2/3 of the way along the lower line segment, and ends at the "bend" in the caret. That creates a Bezier curve that renders as a line segment. The second curve is also a straight line Bezier curve. The second one starts at the bend in the caret and ends at the top left corner.
The arc is also composed of 2 cubic bezier curves, drawn in the same direction as those in the caret symbol. However, the control points for the arc's Bezier curves are chosen so that the resulting curve closely approximates an arc of 3π/2 radians, or 270 degrees, or 3/4 of a cicrcle. The arc is slightly larger than the caret it replaces, and faces the same way.
It's easier to understand if you add a visual representation of the Bezier control points, like this:
The red dots that land on the caret/curve are the endpoints of the 2 Bezier curves. The red dots that are on the outside of the arc are the control points that define the shape of the curves. For the caret shape, the control points are on the lines, which causes the Bezier curve to take a straight line shape.
I used this article to get the control points for the two Bezier curves. I didn't feel like figuring out the math, so I just set the curve on that page's interactive arc renderer to draw 3/8 of a circle, wrote down the coordinates of all the control points, and then flipped them to get the second Bezier curve.
Here is a screenshot from the Bezier Arc approximator web simulation I used to find the Bezier control points:
(In that screenshot, the angle slider is expressed in radians. One half of a 3/4 circle arc is 3/8 of a full circle, or 3/8 of 2π. 2π * 3/8 is about 2.36, so that is the arc angle I chose.)
The CaretToArcAnimation app defines a CaretToArcView class which is a subclass of UIView.
Most of the interesting work is done in the CaretToArcView class.
It has a static var layerClass which returns CAShapeLayer.self. This causes the view's backing layer to be set up as a CAShapeLayer.
class override var layerClass: AnyClass {
return CAShapeLayer.self
}
The CaretToArcView.swift file defines an enum ViewState:
enum ViewState: Int {
case none
case caret
case arc
}
The custom view class has a var viewState of type ViewState. Its initial value is .none, meaning no path is installed in the view.
If you set the state to .caret or .arc, it checks to see if the current layer path is nil. If it is, it installs the appropriate path into the layer without animation.
If the previous path was the other path type, it builds a path with the new shape, and creates a CABasicAnimation with a fromState of the previous path, and toState of the new path. The animation's duration is set using an instance variable, animationDuration. The animation uses ease-in, ease-out timing, although it would be easy to change.
The class also has public function rotate(_ doRotate: Bool). If you call it with doRotate == false, it removes all animations from the view's layer. If you call it with doRotate == true, it adds an infinitely repeating rotation animation to the layer, rotating it 360°, over and over, using linear timing.
The app's view controller drives the settings in the CaretToArcView, and it has logic that prevents the custom view from invoking both animations at the same time (Strange things would happen if you did that. Don't.)
The app's screen looks like the image below.
The view controller disables both the button and the switch during a toggle animation, and disables the "Toggle" button when the rotate switch is turned on and the shape is rotating.

How to get the current animation step from a CAKeyframeAnimation

I'm currently creating a small button with two different states. Every state is represented by two CAShapeLayers and the transition between the states is animated with a series of CAKeyframeAnimations/CABasicAnimations, which all change the path property of the shape layers. My question is how do I animate to a state starting from a current animation (i.e. button is pressed while animating).
Normally, I would ask the presentation layer for the current property value and use an additive animation (as perfectly described here), but since this is a multi-step animation I would have to figure out which step I am currently animating and then chain the appropriate animation to reverse to the previous state. But that is rather tricky (I wanted to have all the animation be removeOnCompletion=false and query the time offset of the animations to figure out which animation is currently active and how far).
Unfortunately, setting the layer speed to 0.0 and just animating the timeOffset back and forth doesn't work either, since I have secondary animations which behave differently in the opposite direction.
Using a custom layer property for the shape layers seems cumbersome, but I could match a progress property more easily to the current animation step.
So after some trial and error I come up with the following "solution": I use a CADisplayLink to get a time update for every frame and by subtracting the current timestamp from the timestamp when the animation started, I can figure out the elapsed animation time, thus the progress (by dividing through animationDuration). That way I can start the reverse animation with an offset. Internally, I calculate the next frame from the keyframe animation and adjust the keyTimes for the new duration.
You can see an implementation of this here

Revolving animation using cocos2d ios

I need a animation like revolving electrons on three orbiter path i the middle of nucleus. I am currently using cocos2d 3.2V for developing my game. I tried to make revolving animation using bezier path but this work for quadrant of circle not make a complete circle animation. How can i achieve this kind of animation using cocos 2d?
Thanks in advance
The center is your parent node, you add the orbiting nodes to the parent with their position at an offset like 40x0, then rotate the parent and the child will rotate around it.
If you need different rotation speeds simply add multiple parent nodes to the center, one for each "planet".
If you want to make the movement an ellipsis you can cheat to some extent. You can have the parent move slightly up and down (or between any other two opposing points) synchronized with the rotation.

How can I reproduce a "box" transition animation in iOS?

I want to build an animated transition between two view controllers in iOS, resembling the "Box" transition in PowerPoint or the "Reflection" transition in Keynote.
You can see it here, at 2:10:
http://youtu.be/1fLQg5hFQQg?t=2m10s
What's the best way to do this?
Thanks!
That would be a complex animation to recreate. You'd need to use a CAAnimationGroup that grouped several different animations running at once. You'd want to animate a rotation around the y axis with the center of rotation lifted off the screen, on both the view controller that is animating away and the view that your are animating into place.
You would have to tweak the transform to make it draw with perspective (you add a small value to the .m34 record in the transform). That's because CA animations are orthographic by default (they don't show perspective.)
The reflections could be created using a special subclass of CALayer that lets you create duplicates of a layer. I'm blanking on the name of that layer subclass at the moment. You'd set up 1 duplicate with a scale of -1 on the y axis to flip it upside down, and a darkening effect. I've never done it myself, but I've seen several examples in books and online.

how can I chain and group Core Animations for different objects?

Say I have 3 CALayers (squares, of different colors, for the sake of example).
And I want to perform the following animation:
layer1 simultaneously translates and scales from position A to position B.
Once layer1 is in its new position and has its new size, layer2 and 3 simultaneously translate from positions C and D to positions E and F, respectively. These layers (2 and 3) also fade in as they translate to E and F. But layer2 also scales as it translates and layer3 just translates (but with a CAKeyframeAnimation so that when it reaches point F it does a little bouncy up-down effect).
For layer 1, I can:
create a CABasicAnimation with the only setting of duration set to,
say 1, and add it to layer1 for key "transform" creating a translate
scale transform and applying it to layer
For layer2 same thing as for layer1 but also setting the opacity.
For layer3 I'd create a keyframe animation. And also set the opacity.
From the above:
Seems like calling layer2 setTransform: followed by setOpacity: results in simultaneous animations. But is this the right way to make sure they are simultaneous or should I use something like CAAnimationGroup?
Does it make sense to translate and scale by concatenating matrices? Does it even make sense to use matrices as opposed to setting positions?
How do I chain these, so that the animations for layer2 and layer3 won't start until layer1 is done and then, layer2's transform and opacity happen at the same time as layer3's keyframe and opacity animations?
I did something extremely similar today.. I was mixing CGAffineTransform and CABasicAnimations and it caused the most awkward error.. anyways.
to set animation to perform a method after completion I'm awful sure I was using:
[self performSelector:#selector(animationDone) withObject:nil afterDelay:0.0];
after I finished making 3 CABasicAnimations in one method I used, CAAnimationGroup to call all 3 at once. After the completion of the 1st animation group it would call the animationDone method to start next method of subsequent animations.
also had to be careful of the:
animationObject.fillMode = kCAFillModeForwards; //b/c of object's coordinate changes
animationObject.removedOnCompletion = NO;

Resources