I have four positions in my CAKeyframeAnimation that are part of a bezier path and my animation is working great along the path.
But now I need the animated sprite to be moved to one specific value of the keyframe position values. how could I realize that?
thnx!
Pull the relevant values out of the CAKeyframeAnimation's keyTimes, values, and potentially timingFunctions arrays, then create a CABasicAnimation out of those.
Related
I am learning a different way to create a custom indicator. Below is a partial code from tutorial using CABasicAnimation to achieve a task.
-(void)spin
{
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
spinAnimation.toValue = [NSNumber numberWithFloat:2*M_PI];
spinAnimation.duration = self.animationDuration;
spinAnimation.delegate = self;
[self.layer addAnimation:spinAnimation forKey:#"spinAnimation"];
}
What is the toValue at line number 2 and what it is used for. When I tried to use
spinAnimation.byValue = [NSNumber numberWithFloat:2*M_PI];
I dont get the idea of these interpolation values.Was searching over the internet but cant still get the whole picture of it..
Please help if you have any ideas about it. All comments are appreciated.
CABasicAnimations can be a little tough to wrap your head around, but the properties associated with the animation really aren't that tough, once you can visualize what they're trying to accomplish. For instance, if I have a red square that represents a layer, and I want to rotate it 360˚ (as you're doing there), then I have to initialize an animation object, tell it what I want to animate, and where I want the animation to go.
The animation you've provided mutates a CALayer's internal matrix so that it is rotated to a given value (in this case, 2 * M_PI, or 360˚) from it's current position (because you haven't specified a fromValue) over the given duration. A given by value tells the animation that over the given period of time, you want the animation to interpolate (or move) by the given value for the provided duration (for instance, you could chunk the animation into 45˚ "blocks" by specifying a byValue of #(M_PI/2)). The default byValue is a division of the difference of the toValue and fromValue over the duration of the animation such that the animation is smooth and continuous.
So, you can think about the animation as going from the layer's initial rotational value, to the layer's new rotational value, by interpolating a given amount or value for a period of time.
You can comprehend "byValue" its means plus the value in original.
For an animation effect perfectly suited to an animation group approach as shown in Brad Larson's answer here, I need the animation to proceed according to inputs. Specifically touch and position of detected touches. It is easy to handle touchesMoved: and to set the position of the elements for every touch but it just isn't smooth like the core animation approach.
Imagine a marble in a grooved track. I want to push the marble along to any position at any speed in one direction or the other. The animation has to do something like that, moving a visual element along a path in response to touches. CAKeyframeAnimation has the path bit exactly but seems to always want to base the transition from frame to frame on time elapsed, not on any other factor, and in one direction.
31 January update - Thanks all for the responses so far however none is really solving the problem. I have a circular menu that is dragged to select an option. All of it needs to move together and I have worked around it by using an view that has a rotational transform applied and the inverse rotational transform applied to its subviews so the icons all rotate with appropriate ferris wheel orientation. It really looks better when the icons are animated along a slightly ovoid path though... the marble description is an attempt to make clear what I'm trying to do. Better perhaps to imagine magnets oriented to repel all travelling in a groove - move one and its neighbours move too but not necessarily in the direction that the dragged magnet moves as the path curves.
Right now the problem is one of following a simple path created with one circle but I'd really like to know how to animate objects along an arbitrary path, position controlled purely by touch with no calculations involving velocity or direction.
You may be able to use the hierarchical nature of timelines in layer trees to achieve what you’re looking for. Objects implementing CAMediaTiming, which include both CAAnimation and CALayer, inherit a timespace from their parent, which they can modify (via scaling, shifting and repeating) and propagate to their children. By setting the speed property of a layer to 0.0 and adjusting the timeOffset property manually, you can decouple that layer (and all of its sublayers) from the usual notion of time.
What you’d do in your case is define the animations for all your menu items to animate their position along your desired CGPath from time t0 to t1, with the appropriate timeOffset on each animation to keep your items appropriately spaced. Note that a beginTime of 0.0 on an animation is usually interpreted as starting from the time the animation was added to its layer, so if you want t0 to be 0.0, you'll probably have to set it to a tiny epsilon > 0.0.
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.beginTime = 1e-100;
animation.duration = 1.0;
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.path = path;
animation.calculationMode = kCAAnimationPaced;
animation.timeOffset = timeOffset;
You’d then set the speed property to 0.0 on a parent layer (containing only these menu items) and update its timeOffset to values between t0 and t1 in response to your touch events.
This approach has two potential caveats. Because you’ve taken over the nature of time on this layer subtree, you probably won’t be able to animate other properties at the same time. Additionally, if you want coasting behavior for a fast flick, you’ll probably need to animate time forward on your own.
If you are targeting iOS 4.0 or greater then you can use the new block based class methods to start animatable changes to view objects. So from your touch event you can initiate an animatable change to properties on the view:
[UIView animateWithDuration:1.0 animations:^{
yourView.alpha = 0.0; // fade out yourView over 1 second
}];
N.B. This could just as easily be a change to another property on the view, like its location. You can animate the following properties on the view this way:
#property frame
#property bounds
#property center
#property transform
#property alpha
#property backgroundColor
#property contentStretch
If you are targetting earlier versions of iOS you will need to use UIView beginAnimations and commitAnimations methods to create an animation block:
[UIView beginAnimations:nil context:context];
[UIView setAnimationDuration:1.0];
yourView.alpha = 0.0;
[UIView commitAnimations];
This stuff works really well and once you start using it, you will have to be careful you don't get addicted. ;)
Update for comment:
You can bind the position of the marble to the location of the touch event. Once you get the touchesEnded event you can then animate the location of the marble using an animation block.
velocity = distance/time. You can try by giving delay according to touches moved and time. You can calculate time between touchesBegan and touchesEnded methods.
I'm not entirely certain I understand exactly what you want to do but... Why not just draw the marble wherever the user's finger is? No animation required.
If you want to keep the marble moving after the user lets go, you need to maintain a concept of momentum and use that to animate the marble slowing down. You could use either CoreAnimation for that or a timer.
I would make a velocity calculation in touches moved, and add it to a local variable that a block can mutate via a timer.
in other words, in touches moved make a velocity calculation bearing in mind the direction of a previous velocity. In touches ended, fire a block that translates the 'marble' decaying the velocity as you disire. If the user moves the marble again, modify the local variable, this will in turn speed up the animation of the marble or slow it down/change direction depending on on the direction of the touch.
I have implemented game application in which i want to draw line between two object.I have drawn line between two objects.But i want to draw with animation.can u advise me which animation i have to used between two points.
Edit:My excatly question is that:
Suppose there is two point like start point(100,100) and endpoint(300,300).I can draw line between this two point but i want to draw line with animation.I mean i can see line start from start point to end point with 2 secon duration.please help me about this question.
+1 for Brad's answer—it'll work—but it looks like you can accomplish the same thing in a more straightforward fashion by animating the strokeStart and strokeEnd properties on CAPathLayer.
See http://oleb.net/blog/2010/12/animating-drawing-of-cgpath-with-cashapelayer/
If you're looking to animate the extension of a line from point A to between points A and B, my recommendation would be to use a CAShapeLayer for this. CAShapeLayer lets you animate the interpolation between two paths with the same number of control points. For an example of this in action, see Joe Ricioppo's post on the subject.
In your case, you'd start with a path that has two control points, both the same point, and use as the final path one that has a control point at the start of the line and one at the end. The line will then animate out as if it were being drawn in a single brush stroke.
The documentation is very poor on this. What's the effect of these? The only thing that seems to work as expected is kCAAnimationLinear. What can I do with the others, for example?
kCAAnimationDiscrete
Each keyframe value is used in turn, no interpolated values are calculated.
This just means that there is no animation at all. And whatever keyframe values that you provide when setting up a keyframe animation will be used like a straight slide show with no transitions.
kCAAnimationPaced
Keyframe values are interpolated to produce an even pace throughout the animation.
This applies a timing curve to your animation from one keyframe to another, producing a slight cadence to the animation. Similar in effect to the CAMediaTimingFunction kCAMediaTimingFunctionEaseInEaseOut.
The calculationMode property is applied to all keyframe transitions within the entire animation.
Individual timing functions can be specified for each keyframe transition by providing an NSArray of CAMediaTimingFunction instances and passing it to the timingFunctions property of the animation. The array of timing functions must match the array of keyframes in number for it to work.
I am trying to figure out how can you drag an image while constraining its movement along a certain path.
I tried several tricks including animation along a path, but couldn't get the animation to play and pause and play backwards - so that seems out of the question.
Any ideas ? anyone ?
What you're basically trying to do is match finger movement to a 'translation' transition.
As the user touches down and starts to move their finger you want to use the current touch point value to create a translation transform which you apply to your UIImageView. Here's how you would do it:
On touch down, save the imageview's starting x,y position.
On move, calculate the delta from old point to new one. This is where you can clamp the values. So you can ignore, say, the y change and only use the x deltas. This means that the image will only move left to right. If you ignore the x and use y, then it only moves up and down.
Once you have the 'new' calculated/clamped x,y values, use it to create a new transform using CGAffineTransformMakeTranslation(x, y). Assign this transform to the UIImageView. The image moves to that place.
Once the finger lifts, figure out the delta from the original starting x,y, point and the lift-off point, then adjust the ImageView's bounds and reset the transform to CGAffineTransformIdentity. This doesn't move the object, but it sets it so subsequent accesses to the ImageView use the actual position and don't have to keep adjusting for transforms.
Moving along on a grid is easy too. Just round out the x,y values in step 2 so they're a multiple of the grid size (i.e. round out to every 10 pixel) before you pass it on to make the translation transform.
If you want to make it extra smooth, surround the code where you assign the transition with UIView animation blocks. Mess around with the easing and timing settings. The image should drag behind a bit but smoothly 'rubber-band' from one touch point to the next.
See this Sample Code : Move Me