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. Specific
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.
velocity = distance/time. You can try by giving delay according to touches moved and time. You can calculate time between touchesBegan and touchesEnded methods.
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.
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.
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.