How do I restart a CALayer animation going on? - ios

Apple mentions these two methods for pausing and resuming a CALayer animation going on
-(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;
}
but how do I restart the animation?
this other SO question has nothing to do with what I need.

I have adopted code from blog in ViewController. Take a look at method [resetEmitterLayer:]. In your case all you need to do to reset animation is as follow:
Remove emitter layer from super layer.
Recreate emitter layer.
Add new emitter layer to the layer tree.
See ViewController implementation below
#import "EmitterViewController.h"
#import <QuartzCore/QuartzCore.h>
#interface EmitterViewController ()
#property (nonatomic, strong) CAEmitterLayer *emitterLayer;
#end
#implementation EmitterViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.emitterLayer = [self createEmitterLayer];
[self.view.layer addSublayer:self.emitterLayer];
}
-(CAEmitterLayer *)createEmitterLayer {
CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.origin.y);
emitterLayer.emitterZPosition = 10;
emitterLayer.emitterSize = CGSizeMake(self.view.bounds.size.width, 0);
emitterLayer.emitterShape = kCAEmitterLayerSphere;
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.scale = 0.1;
emitterCell.scaleRange = 0.2;
emitterCell.emissionRange = (CGFloat)M_PI_2;
emitterCell.lifetime = 5.0;
emitterCell.birthRate = 10;
emitterCell.velocity = 200;
emitterCell.velocityRange = 50;
emitterCell.yAcceleration = 250;
emitterCell.contents = (id)[[UIImage imageNamed:#"WaterDrop.png"] CGImage];
emitterLayer.emitterCells = [NSArray arrayWithObject:emitterCell];
return emitterLayer;
}
-(IBAction)pauseEmitterLayer:(id)sender
{
CFTimeInterval pausedTime = [self.emitterLayer convertTime:CACurrentMediaTime() fromLayer:nil];
self.emitterLayer.speed = 0.0;
self.emitterLayer.timeOffset = pausedTime;
}
-(IBAction)resumeEmitterLayer:(id)sender
{
CFTimeInterval pausedTime = [self.emitterLayer timeOffset];
self.emitterLayer.speed = 1.0;
self.emitterLayer.timeOffset = 0.0;
self.emitterLayer.beginTime = 0.0;
CFTimeInterval timeSincePause = [self.emitterLayer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
self.emitterLayer.beginTime = timeSincePause;
}
-(IBAction)resetEmitterLayer:(id)sender
{
[self.emitterLayer removeFromSuperlayer];
self.emitterLayer = [self createEmitterLayer];
[self.view.layer addSublayer:self.emitterLayer];
}
#end

Related

CAAnimation with timeOffSet

I have an animation with 5.0 second duration. While an animation is being played, i pause it at 2.0 second and do some other things (moving to other viewcontroller, push, pop...). And then i back to this viewcontroller and continue animation from second 2.0. I made it work smooth with timeOffSet = 2.0, but still have an issue : It has 2 seconds surplus, that is the first 2 second of an animation.....How can i remove it ?
Animation with timeOffSet process:
Duplicated answer
-(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;
}

Bounce at the end of CABasicAnimation

Setup animation:
CABasicAnimation *noteAnimation = [CABasicAnimation animation];
noteAnimation.keyPath = #"position.x";
noteAnimation.byValue = [NSNumber numberWithFloat:CGRectGetWidth(self.view.frame) - 50];
noteAnimation.duration = 1.f;
noteAnimation.fillMode = kCAFillModeBoth;
noteAnimation.removedOnCompletion = NO;
noteAnimation.autoreverses = YES;
noteAnimation.repeatCount = HUGE;
noteAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.greenSquareView.layer addAnimation:noteAnimation forKey:#"noteAnimation"];
To pause the acting animation I use:
CFTimeInterval pausedTime = [view.layer convertTime:CACurrentMediaTime() fromLayer:nil];
view.layer.speed = 0.0;
view.layer.timeOffset = pausedTime;
But in this case view stops with bouncing.
Full project on BitBucket.

Unexpected CALayer Vertical Flipping on 3D Rotation 'Bounce'

I am trying to create a flip transition that contains a grid of CALayer cells that flip over and 'bounce' after they have done so. However, I am encountering a weird vertical flipping issue shown in this video. Without the 'bounce' animation at the end, the animation works as expected, so it's nothing to do with the image itself, or the splicing of the image into the individual cell layers before animating.
Animation Code (for each individual CALayer cell)
#interface transitionCell : CALayer
#property (nonatomic) UIImage* startImage;
#property (nonatomic) UIImage* endImage;
#property (nonatomic, readonly) BOOL animationDidFinish;
#property (nonatomic, readonly) BOOL isAnimating;
#property (nonatomic) CGFloat zRotateFactor;
#end
#implementation transitionCell
-(void) setStartImage:(UIImage *)startImage {
self.contents = (__bridge id)startImage.CGImage;
}
-(void) startAnimationWithDuration:(CGFloat)duration { // First half of rotation
_isAnimating = YES;
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotation.fromValue = #(0);
yRotation.toValue = #(M_PI*0.5);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
zRotation.fromValue = #(0);
zRotation.toValue = #(zRotateMax*_zRotateFactor); // zRotateMax is M_PI*0.2 & _zRotateFactor is a value between -1 and 1.
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = duration*0.5;
rotations.delegate = self;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.animations = #[yRotation, zRotation];
[self addAnimation:rotations forKey:#"startRotation"];
}
-(void) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { // Second half of rotation
if (anim == [self animationForKey:#"startRotation"] && flag) {
self.contents = (__bridge id)_endImage.CGImage;
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotation.toValue = #(M_PI);
yRotation.fromValue = #(M_PI*0.5);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
zRotation.toValue = #(0);
zRotation.fromValue = #(zRotateMax*_zRotateFactor);
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = anim.duration;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.animations = #[yRotation, zRotation];
[self addAnimation:rotations forKey:#"endRotate"];
} else if (anim == [self animationForKey:#"endRotate"] && flag) { // The 'Bounce' animation (the one causing the issues)
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotation.toValue = #(M_PI+(M_PI*0.2));
yRotation.fromValue = #(M_PI);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
zRotation.fromValue = #(0);
zRotation.toValue = #(-zRotateMax*_zRotateFactor*0.2);
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = 0.2;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.autoreverses = YES;
rotations.animations = #[yRotation, zRotation];
[self addAnimation:rotations forKey:#"endRotate2"];
} else if (anim == [self animationForKey:#"endRotate2"] && flag) {
_animationDidFinish = YES;
}
}
#end
Initialisation Code (In the parent UIViewController)
-(instancetype) initWithStartImage:(UIImage*)startImage endImage:(UIImage*)endImage {
if (self = [super init]) {
CGFloat baseNodeHeight = screenWidth()/baseNumberNodesWidth;
numNodesHeight = roundf(screenHeight()/baseNodeHeight);
numNodesWidth = roundf(screenWidth()/baseNodeHeight);
moveUpdateFreq = moveBaseUpdateFreq/(numNodesWidth*numNodesHeight);
cellMoveUpdateFreq = cellMoveBaseUpdateFreq/(numNodesWidth*numNodesHeight);
CGFloat const nodeWidth = screenWidth()/numNodesWidth;
CGFloat const nodeHeight = screenHeight()/numNodesHeight;
transition = (transitionType)arc4random_uniform(transitionTypeCount);
cellArray = [NSMutableArray array];
for (int x = 0; x < numNodesWidth; x++) {
[cellArray addObject:[NSMutableArray array]];
for (int y = 0; y < numNodesHeight; y++) {
transitionCell* c = [transitionCell layer];
c.frame = (CGRect){{x*nodeWidth, y*nodeHeight}, {nodeWidth, nodeHeight}};
CGRect startSubRect = {{c.frame.origin.x, screenHeight()-c.frame.origin.y-c.frame.size.height}, c.frame.size};
CGRect endSubRect = {{c.frame.origin.x, c.frame.origin.y}, c.frame.size};
c.startImage = [startImage imageFromSubRect:startSubRect];
c.endImage = [[endImage flippedVerticalImage] imageFromSubRect:endSubRect];
c.zRotateFactor = -(((CGFloat)y-((CGFloat)numNodesHeight*0.5))/(CGFloat)numNodesHeight);
[self.view.layer addSublayer:c];
[cellArray[x] addObject:c];
}
}
}
return self;
}
Any ideas what I'm doing wrong?
Well, I can only assume this is a bug, but nonetheless I have found a quick fix. By adding a xRotation animation set to go from pi to pi on the 'bounce' animation, the problem is fixed. For example, in the bounce animation code:
// Bounce animation
CABasicAnimation* yRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.y"];
yRotation.toValue = #(M_PI+(M_PI*bounceFactor));
yRotation.fromValue = #(M_PI);
CABasicAnimation* zRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
zRotation.fromValue = #(0);
zRotation.toValue = #(-zRotateMax*_zRotateFactor*bounceFactor);
// New animation, set to do nothing on the x-axis
CABasicAnimation* xRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.x"];
xRotation.toValue = #(M_PI);
xRotation.fromValue = #(M_PI);
CAAnimationGroup* rotations = [CAAnimationGroup animation];
rotations.duration = 0.2;
rotations.removedOnCompletion = NO;
rotations.fillMode = kCAFillModeForwards;
rotations.delegate = self;
rotations.autoreverses = YES;
rotations.animations = #[yRotation, zRotation, xRotation];
[self addAnimation:rotations forKey:#"endRotate2"];

Stop animateWithDuration

I have a cycle animate in viewController
- (void)moveAnimating
{
[UIView animateWithDuration:2.0f animations:^{
_backgroundView.center = CGPointMake(self.center.x , self.center.y - kMoveDistanceHeight);
} completion:^(BOOL finished) {
if (_backgroundView.animating)
{
[_backgroundView moveAnimating];
}
}];
}
I want stop this animate When the viewController viewWillDisappear:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
_backgroundView.animating = NO;
[_backgroundView.layer removeAllAnimations];
}
Because the animation is conflict with dismissViewcontrollerAnimation.
Question:
[_backgroundView.layer removeAllAnimations];
not work...
How to stop the animation?
Help me,thanks.
You are canceling animations correctly:
[_backgroundView.layer removeAllAnimations];
But you might forget about importing QuartzCore.h:
#import <QuartzCore/QuartzCore.h>
If it doesn't help try this:
[CATransaction begin];
[_backgroundView.layer removeAllAnimations];
[CATransaction commit];
If it doesn't help try to add this line to the code above:
[CATransaction flush];
Te solution from #KlimczakM didn't work for me.
I'm running an 'animateWithDuration' block that moves and image, and I use the next code to pause and resume the animation:
-(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;
}
This is from the Apple Documentation at this link.

Suspend Particles in CAEmitterLayer

I have the following code that adds particles to a UIView named ParentView at the center of some other UIView:
CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
emitterLayer.emitterPosition = CGPointMake(view.center.x, view.center.y - view.frame.size.height / 3);
emitterLayer.emitterZPosition = 10;
emitterLayer.emitterSize = CGSizeMake(view.bounds.size.width, 0);
emitterLayer.emitterShape = kCAEmitterLayerSphere;
CAEmitterCell *emitterCell = [CAEmitterCell emitterCell];
emitterCell.scale = 0.1;
emitterCell.scaleRange = 0.2;
emitterCell.emissionRange = 45;
emitterCell.lifetime = 0.75;
emitterCell.birthRate = 60;
emitterCell.velocity = 200;
emitterCell.velocityRange = 50;
emitterCell.yAcceleration = 250;
emitterCell.contents = (id)[[UIImage imageNamed:#"particle.png"] CGImage];
emitterLayer.emitterCells = [NSArray arrayWithObject:emitterCell];
[parentView.layer addSublayer:emitterLayer];
Everything works well but now I want to pause or suspend the animation, so the particles "freeze". Can this be done?
I think pause the layer will work,
-(void)pauseLayer:(CALayer*)layer
{
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
layer.speed = 0.0;
layer.timeOffset = pausedTime;
}

Resources