Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone animation based on input values (touches) not time

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.

like image 671
Adam Eberbach Avatar asked Jan 18 '11 03:01

Adam Eberbach


2 Answers

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.

like image 108
Pivot Avatar answered Oct 06 '22 01:10

Pivot


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.

like image 21
RedBlueThing Avatar answered Oct 05 '22 23:10

RedBlueThing