Draw a circle on a Long Press event - ios

I am drawing a circle on the screen as the user taps on a button. The animation duration is already set and also the from and to value are also set.
What I want to achieve is that somehow the animation should commence as the user long presses the button and continues till he is maintaining the tap on the screen i.e for the duration of Long Press.
As soon as the user lifts his finger the circle should stop to the point to where it has been completed till now.
Here is my code:
-(void)startCircularAnimation{
int radius = 50;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor redColor].CGColor;
circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:circle];
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 15.0;
drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:counter/drawAnimation.duration];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[circle addAnimation:drawAnimation forKey:#"draw"];
}
This method performs animation and the from value is calculated from a timer which I started on the touch began case of the long press handler method. I am not able to get the perfect duration for the long press.
The long press event methods is something like this.
- (void)_handleLongPressGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer{
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
{
counter = 0;
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(incrementCounter) userInfo:nil repeats:YES];
}
case UIGestureRecognizerStateEnded:{
NSLog(#"State ended");
[timer invalidate];
break;
}
case UIGestureRecognizerStateCancelled:{
NSLog(#"State cancelled");
break;
}
case UIGestureRecognizerStateFailed:
{
break;
}
default:
break;
}
}
and the increment counter method is as follows
- (void)incrementCounter {
counter++;
[self startCircularAnimation];
}
This is not giving me the desired effect for drawing the circle till the user has his finger on the screen.
Please suggest something in the code to get the desired functionality.
Thanks in advance.

You'll want to follow apples guidelines https://developer.apple.com/library/ios/qa/qa1673/_index.html
So in your interface i'd declare the following
#interface ViewController ()
#property (nonatomic, strong) CAShapeLayer *circle;
#property (nonatomic, strong) CABasicAnimation *drawAnimation;
#property (strong, nonatomic) IBOutlet UIButton *circleButton;
#end
Then in view did load
- (void)viewDidLoad
{
[super viewDidLoad];
int radius = 50;
self.circle = [CAShapeLayer layer];
// Make a circular shape
self.circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
self.circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
// Configure the apperence of the circle
self.circle.fillColor = [UIColor clearColor].CGColor;
self.circle.strokeColor = [UIColor redColor].CGColor;
self.circle.lineWidth = 5;
self.circle.strokeEnd = 0.0f;
// Add to parent layer
[self.view.layer addSublayer:_circle];
// Target for touch down (hold down)
[self.circleButton addTarget:self action:#selector(startCircleAnimation) forControlEvents:UIControlEventTouchDown];
// Target for release
[self.circleButton addTarget:self action:#selector(endCircleAnimation) forControlEvents:UIControlEventTouchUpInside];
/**
Don't start Animation in viewDidLoad to achive the desired effect
*/
}
Function to start the animation and resume it (probably needs a better name)
-(void)startCircleAnimation{
if (_drawAnimation) {
[self resumeLayer:_circle];
} else {
[self circleAnimation];
}
}
Function to end animation
-(void)endCircleAnimation{
[self pauseLayer:_circle];
}
Function to generate animation
- (void)circleAnimation
{
// Configure animation
self.drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
self.drawAnimation.duration = 10.0;
self.drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
self.drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
// Set your to value to one to complete animation
self.drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// Experiment with timing to get the appearence to look the way you want
self.drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[self.circle addAnimation:_drawAnimation forKey:#"draw"];
}
Pause and stop functions from apple
- (void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}
- (void)resumeLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer timeOffset];
layer.speed = 1.0;
layer.timeOffset = 0.0;
layer.beginTime = 0.0;
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
layer.beginTime = timeSincePause;
}
The main point you'd want to take from this is that your not keeping account of how long the button is pressed for your just keeping account of the events that are sent from the button UIControlEventTouchDown and UIControlEventTouchUpInside
Edit:Gif

I think you should make your from value related to value of counter, this will make the drawing start from what it was left behind last time the user lift his finger.
You should also make your timer's time interval smaller, 1 second is too long, 0.1 second will be better.

Related

How to get coordinates of view during animation?

I've created CAShapeLayer and added Bezier Cubic path(with addCurveToPoint:controlPoint1:controlPoint2: function) and added a little view on start point of that curve. After that I've created CAKeyFrameAnimation and moved that little view from start point of curve to the end point with animation. I want to get the center point of that view during animation. Any ideas?
CGPoint start, p, p1,p2;
start = CGPointMake(0, 150);
p = CGPointMake(300, 150);
p1 = CGPointMake(50, 225);
p2 = CGPointMake(250, 75);
[self.bezierPath moveToPoint:start];
[self.bezierPath addCurveToPoint:p
controlPoint1:p1
controlPoint2:p2];
NSLog(#"self bezierpath = %#",self.bezierPath);
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = self.bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.lineWidth = 3.f;
[self.yellowView.layer addSublayer:shapeLayer];
self.sliderThumb = [[UIView alloc]initWithFrame:(CGRect){0,0,20,20}];
self.sliderThumb.backgroundColor = [UIColor blackColor];
self.sliderThumb.center = CGPointMake(0, 150);
[self.yellowView addSubview:self.sliderThumb];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
animation.keyPath = #"position";
animation.duration = 4.f;
animation.path = self.bezierPath.CGPath;
[self.sliderThumb.layer addAnimation:animation forKey:nil];
CALayer *layer = self.sliderThumb.layer.presentationLayer;
NSLog(#"layer coordinates %f", layer.frame.origin.x);
`
The presentationLayer of the view's layer is a CALayer which captures where the layer is mid-animation.
For example, if you want to get updates frame by frame, set up a CADisplayLink and examine the presentationLayer in the display link's handler:
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.duration = 4.0f;
animation.path = self.bezierPath.CGPath;
animation.delegate = self; // so we know when animation finished
[self startDisplayLink]; // start display link
[self.sliderThumb.layer addAnimation:animation forKey:nil];
With:
#property (nonatomic, weak) CADisplayLink *displayLink;
And:
// Called when animation stops (assuming you set the `delegate` for the animation).
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
[self stopDisplayLink:self.displayLink];
}
/// Start the display link.
- (void)startDisplayLink {
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(handleDisplayLink:)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
self.displayLink = displayLink;
}
/// Stop the display link.
///
/// #parameter displayLink The display link to be stopped.
- (void)stopDisplayLink:(CADisplayLink *)displayLink {
[displayLink invalidate];
}
/// The display link handler.
///
/// Called once for each frame of the animation.
///
/// #parameter displayLink The display link being handled.
- (void)handleDisplayLink:(CADisplayLink *)displayLink {
CALayer *layer = self.sliderThumb.layer.presentationLayer;
NSLog(#"layer coordinates %f", layer.frame.origin.x);
}

Animate path of shape layer

I'm stumped by what I thought would be a simple problem.
I'd like to draw views connected by lines, animate the position of the views and have the connecting line animate too. I create the views, and create a line between them like this:
- (UIBezierPath *)pathFrom:(CGPoint)pointA to:(CGPoint)pointB {
CGFloat halfY = pointA.y + 0.5*(pointB.y - pointA.y);
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];
[linePath addLineToPoint:CGPointMake(pointA.x, halfY)];
[linePath addLineToPoint:CGPointMake(pointB.x, halfY)];
[linePath addLineToPoint:pointB];
return linePath;
}
-(void)makeTheLine {
CGPoint pointA = self.viewA.center;
CGPoint pointB = self.viewB.center;
CAShapeLayer *lineShape = [CAShapeLayer layer];
UIBezierPath *linePath=[self pathFrom:pointA to:pointB];
lineShape.path=linePath.CGPath;
lineShape.fillColor = nil;
lineShape.opacity = 1.0;
lineShape.strokeColor = [UIColor blackColor].CGColor;
[self.view.layer addSublayer:lineShape];
self.lineShape = lineShape;
}
It draws just how I want it to. My understanding from the docs is that I am allowed to animate a shape's path by altering it in an animation block, like this:
- (void)moveViewATo:(CGPoint)dest {
UIBezierPath *destPath=[self pathFrom:dest to:self.viewB.center];
[UIView animateWithDuration:1 animations:^{
self.viewA.center = dest;
self.lineShape.path = destPath.CGPath;
}];
}
But no dice. The view position animates as expected, but the line connecting to the other view "jumps" right away to the target path.
This answer implies that what I'm doing should work. And this answer suggests a CABasic animation, which seems worse to me since (a) I'd then need to coordinate with the much cooler block animation done to the view, and (b) when I tried it this way, the line didn't change at all....
// worse way
- (void)moveViewATo:(CGPoint)dest {
UIBezierPath *linePath=[self pathFrom:dest to:self.viewB.center];
[UIView animateWithDuration:1 animations:^{
self.viewA.center = dest;
//self.lineShape.path = linePath.CGPath;
}];
CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:#"path"];
morph.duration = 1;
morph.toValue = (id)linePath.CGPath;
[self.view.layer addAnimation:morph forKey:nil];
}
Thanks in advance.
Thanks all for the help. What I discovered subsequent to asking this is that I was animating the wrong property. It turns out, you can replace the layer's shape in an animation, like this:
CABasicAnimation *morph = [CABasicAnimation animationWithKeyPath:#"path"];
morph.duration = 1;
morph.fromValue = (__bridge id)oldPath.path;
morph.toValue = (__bridge id)newPath.CGPath;
[line addAnimation:morph forKey:#"change line path"];
line.path=linePath.CGPath;
I guess this is all you need:
#import "ViewController.h"
#interface ViewController ()
//the view to animate, nothing but a simple empty UIView here.
#property (nonatomic, strong) IBOutlet UIView *targetView;
#property (nonatomic, strong) CAShapeLayer *shapeLayer;
#property NSTimeInterval animationDuration;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//the shape layer appearance
self.shapeLayer = [[CAShapeLayer alloc]init];
self.shapeLayer.strokeColor = [UIColor blackColor].CGColor;
self.shapeLayer.fillColor = [UIColor clearColor].CGColor;
self.shapeLayer.opacity = 1.0;
self.shapeLayer.lineWidth = 2.0;
[self.view.layer insertSublayer:self.shapeLayer below:self.targetView.layer];
//animation config
self.animationDuration = 2;
}
- (UIBezierPath *)pathFrom:(CGPoint)pointA to:(CGPoint)pointB {
CGFloat halfY = pointA.y + 0.5*(pointB.y - pointA.y);
UIBezierPath *linePath=[UIBezierPath bezierPath];
[linePath moveToPoint: pointA];
[linePath addLineToPoint:CGPointMake(pointA.x, halfY)];
[linePath addLineToPoint:CGPointMake(pointB.x, halfY)];
[linePath addLineToPoint:pointB];
return linePath;
}
- (void) moveViewTo: (CGPoint) point {
UIBezierPath *linePath= [self pathFrom:self.targetView.center to:point];
self.shapeLayer.path = linePath.CGPath;
//Use CAKeyframeAnimation to animate the view along the path
//animate the position of targetView.layer instead of the center of targetView
CAKeyframeAnimation *viewMovingAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
viewMovingAnimation.duration = self.animationDuration;
viewMovingAnimation.path = linePath.CGPath;
//set the calculationMode to kCAAnimationPaced to make the movement in a constant speed
viewMovingAnimation.calculationMode =kCAAnimationPaced;
[self.targetView.layer addAnimation:viewMovingAnimation forKey:viewMovingAnimation.keyPath];
//draw the path, animate the keyPath "strokeEnd"
CABasicAnimation *lineDrawingAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
lineDrawingAnimation.duration = self.animationDuration;
lineDrawingAnimation.fromValue = [NSNumber numberWithDouble: 0];
lineDrawingAnimation.toValue = [NSNumber numberWithDouble: 1];
[self.shapeLayer addAnimation:lineDrawingAnimation forKey:lineDrawingAnimation.keyPath];
//This part is crucial, update the values, otherwise it will back to its original state
self.shapeLayer.strokeEnd = 1.0;
self.targetView.center = point;
}
//the IBAction for a UITapGestureRecognizer
- (IBAction) viewDidTapped:(id)sender {
//move the view to the tapped location
[self moveViewTo:[sender locationInView: self.view]];
}
#end
Some explanation:
For UIViewAnimation, the property value is changed when the
animation is completed. For CALayerAnimation, the property value is
never change, it is just an animation and when the animation is
completed, the layer will go to its original state (in this case, the
path).
Putting self.lineShape.path = linePath.CGPath doesn't work is
because self.linePath is a CALayer instead of a UIView, you
have to use CALayerAnimation to animate a CALayer
To draw a path, it's better to animate the path drawing with keyPath
strokeEnd instead of path. I'm not sure why path worked in the
original post, but it seems weird to me.
CAKeyframeAnimation (instead of CABasicAnimation or UIViewAnimation) is used to animate the view along the path. (I guess you would prefer this to the linear animation directly from start point to end point). Setting calculationMode to kCAAnimationPaced will give a constant speed to the animation, otherwise the view moving will not sync with the line drawing.

Using CAShapeAnimation Progressively Using Taps iOS

As I tap, I would like a counter to increment to increase the arc size of a circle.
The line should animate to the next size and stay there until the next increment is made (lets say from a tap - that i already measure elsewhere), then animate to the next size etc..
Seee the example below
Here is some code I've found so far...
_radius = 150;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*_radius, 2.0*_radius)
cornerRadius:_radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(self.target.frame)-_radius,
CGRectGetMidY(self.target.frame)-_radius);
// Configure the appearence of the circle
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:circle];...
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 0.3; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = NO; // Remain stroked after the animation..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
//drawAnimation.toValue = [NSNumber numberWithFloat:0.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
Animating Drawing Values
I've done similar animations in CoreGraphics, so you may want to take a look at that as an alternative if you can't get CAShapeAnimation to work for you. I've got a progress view here that has this sort of animation except in a linear fashion.
- (void)setProgress:(CGFloat)progress {
self.progressToAnimateTo = progress;
if (self.animationTimer) {
[self.animationTimer invalidate];
}
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:0.008 target:self selector:#selector(incrementAnimatingProgress) userInfo:nil repeats:YES];
}
- (void)incrementAnimatingProgress {
if (_progress >= self.progressToAnimateTo-0.01 && _progress <= self.progressToAnimateTo+0.01) {
_progress = self.progressToAnimateTo;
[self.animationTimer invalidate];
[self setNeedsDisplay];
} else {
_progress = (_progress < self.progressToAnimateTo) ? _progress + 0.01 : _progress - 0.01;
[self setNeedsDisplay];
}
}
This technique of using another property such as progressToAnimateTo and incrementing the progress value before redrawing could also be harnessed to then redraw the progress of a circle.
Resources
I might also add that there are other implementations of what you're doing here. MBProgressHUD and EVCircularProgressView have this sort of effect.

Animate rotation and location of view at same time

What is the best way to rotate a view and move its location at the same time?
I want this view to rotate 1 full revolution while sliding to the left, then rotate the other direction while sliding back to its original position. I got the first part of this working (rotate while sliding to left), but when I try to rotate while sliding back to the right, the view jumps back to its original spot without animating.
What am I doing wrong?
// step 1 (this works)
[UIView animateWithDuration:0.3f animations:^{
self.number.transform = CGAffineTransformMakeRotation(DegreesToRadians(180));
self.number.transform = CGAffineTransformMakeTranslation(-160, 0);
self.number.center = CGPointMake(self.number.center.x - 160, self.number.center.y);
} completion:nil];
// step 2 (this doesn't work)
[UIView animateWithDuration:0.2f animations:^{
self.number.transform = CGAffineTransformMakeRotation(DegreesToRadians(180));
self.number.transform = CGAffineTransformMakeTranslation(160, 0);
self.number.center = CGPointMake(self.number.center.x + 160, self.number.center.y);
} completion:nil];
I got it to work using the following code. My view had a constraint to the left side of the superview, to which I added the IBOutlet, leftCon. It's initial value was 160, so the translation moves it all the way to the left edge of the screen.
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#define animationTime 1.0
#interface ViewController ()
#property (nonatomic) CGFloat leftConstant;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.leftConstant = self.leftCon.constant;
}
- (IBAction)rotate:(UIButton *)sender {
if (self.leftCon.constant == self.leftConstant) {
[self.view removeConstraint:self.leftCon];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = #(-M_PI * 2.0);
rotationAnimation.duration = animationTime;
CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
translationAnimation.fromValue = [NSValue valueWithCGPoint:self.number.center];
translationAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(self.number.frame.size.width/2.0, self.number.center.y)];
translationAnimation.duration = animationTime;
[self.number.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[self.number.layer addAnimation:translationAnimation forKey:#"translationAnimation"];
self.leftCon.constant = 0;
[self.view addConstraint:self.leftCon];
}else{
[self.view removeConstraint:self.leftCon];
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = #(M_PI * 2.0);
rotationAnimation.duration = animationTime;
CABasicAnimation *translationAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
translationAnimation.fromValue = [NSValue valueWithCGPoint:self.number.center];
translationAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(self.leftConstant+ self.number.frame.size.width/2.0, self.number.center.y)];
translationAnimation.duration = animationTime;
[self.number.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[self.number.layer addAnimation:translationAnimation forKey:#"translationAnimation"];
self.leftCon.constant = self.leftConstant;
[self.view addConstraint:self.leftCon];
}
}
You probably want to place your second animation as the completion block in the first animation.

CAKeyframeAnimation Manual Progress

I have a UIView whose backing layer has a CAKeyframeAnimation with a simple straight line path set as its `path`.
Can I have the animation "frozen", so to speak, and manually change its progress?
For example:
If the path is 100 points in length, setting the progress (offset?) to 0.45 should have the view move 45 points down the path.
I remember seeing an article that did something similar (moving a view along a path based on the value from a slider) via CAMediaTiming interfaces, but I haven't been able to find it, even after a few hours of searching. If I'm approaching this in a completely wrong way, please do let me know. Thanks.
Here's some sample code, if the above isn't clear enough.
- (void)setupAnimation
{
CAKeyFrameAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:_label.layer.position];
[path addLineToPoint:(CGPoint){200, 200}];
animation.path = path.CGPath;
animation.duration = 1;
animation.autoreverses = NO;
animation.removedOnCompletion = NO;
animation.speed = 0;
// _label is just a UILabel in a storyboard
[_label.layer addAnimation:animation forKey:#"LabelPathAnimation"];
}
- (void)sliderDidSlide:(UISlider *)slider
{
// move _label along _animation.path for a distance that corresponds to slider.value
}
This is based on what Jonathan said, only a bit more to the point. The animation is set up correctly, but the slider action method should be as follows:
- (void)sliderDidSlide:(UISlider *)slider
{
// Create and configure a new CAKeyframeAnimation instance
CAKeyframeAnimation *animation = ...;
animation.duration = 1.0;
animation.speed = 0;
animation.removedOnCompletion = NO;
animation.timeOffset = slider.value;
// Replace the current animation with a new one having the desired timeOffset
[_label.layer addAnimation:animation forKey:#"LabelPathAnimation"];
}
This will make the label move along the animation's path based on timeOffset.
Yes you can do this with the CAMediaTiming interface. You can set the speed of the layer to 0 and manualy set the timeOffset. Example of a simple pause/resume method:
- (void)pauseAnimation {
CFTimeInterval pausedTime = [yourLayer convertTime:CACurrentMediaTime() fromLayer:nil];
yourLayer.speed = 0.0;
yourLayer.timeOffset = pausedTime;
}
- (void)resumeAnimation {
CFTimeInterval pausedTime = [yourLaye timeOffset];
if (pausedTime != 0) {
yourLayer.speed = 1.0;
yourLayer.timeOffset = 0.0;
yourLayer.beginTime = 0.0;
CFTimeInterval timeSincePause = [yourLayer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
yourLayer.beginTime = timeSincePause;
}
}

Resources