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.
Related
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.
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.
I have a transparent png inside a UIImageView (self.myImage) that I want to rotate around its center point.
The code should be pretty simple:
[self.myImage.layer setAnchorPoint:CGPointMake(0.5, 0.5)];
[UIView animateWithDuration:1.0 animations:^{
[self.myImage setTransform:CGAffineTransformMakeRotation(angle)];
}];
The image rotates at the right speed/time and at the right angle, but its position gets shifted. Here's an example of what's happening:
The gray square is just to show position in the screen. The transparent png (contained in a UIImageView) is the other figure. The white dotted lines show the center of the UIImageView. The left side of the image shows the original position of the image, the right side shows the image after being rotated with the above code (which gets shifted a little down to the right). The black and white circles are in the center of the image file.
Is there something that I'm missing? As far as I understand, the first line above is not required because those are the defaults. Do I have to set/unset something in the storyboard/programmatically?
You just try this code
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1 / 500.0;
transform = CATransform3DRotate(transform, .0 * M_PI_2, 1, 0, 0);/*Here the angle of transform set (Here angle set as 0)*/
self.transformView.layer.transform = transform;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.duration = 3.0;
animation.repeatCount = HUGE_VALF;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.discView.layer addAnimation:animation forKey:#"transform.rotation.z"];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.discView.layer removeAllAnimations];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
In this you just change transformView with another view or ImageView
I found out that the code I need to accomplish what I needed was simpler than the one given by #Albin Joseph, but it did point me to the right direction.
My animation needs to pick up where it left off and rotate to a new position. Some times it will be animated and sometimes it won't. So hence the code:
CGFloat duration = animated ? 0.5 : 0.01;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [[self.turnIndicatorImage.layer presentationLayer] valueForKeyPath:#"transform.rotation.z"];
animation.toValue = angle;
animation.duration = duration;
animation.fillMode = kCAFillModeForwards;
animation.repeatCount = 0;
animation.removedOnCompletion = NO;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.turnIndicatorImage.layer addAnimation:animation forKey:#"transform.rotation.z"];
As far as I can see, Apple wants us to move away from CGAffineTransform animations and into animations using:
myView.layoutConstraint.constant = someNewValue;
[myView layoutIfNeeded];
for animations that involve a translation of a view.
It also seems we should be now using CABasicAnimation animations for scale and rotation (and sometimes opacity) because it animates the view's layer and not the view and in doing so, plays nicely with auto layout.
I used the following code to apply an opacity and scale animation that worked beautifully:
[UIView animateWithDuration:0.1f animations:^{
// first animation
self.myMeButton.alpha = 1;
self.myMeButton.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2f animations:^{
// second animation
self.myButton.transform = CGAffineTransformMakeScale(1, 1);
}];
}];
Of course auto layout plays havoc with the scale animation and so I am trying to find an alternative way to do it. So far, I have come up with this:
[CATransaction begin]; {
[CATransaction setCompletionBlock:^{
// code for when animation completes
self.pickMeButton.alpha = 1;
CABasicAnimation *scaleDown = [CABasicAnimation animationWithKeyPath:#"scale"];
scaleDown.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
scaleDown.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
scaleDown.duration = 0.1;
[self.myButton.layer addAnimation:scaleDown forKey:nil];
}];
// describe animations:
CABasicAnimation* scaleUp = [CABasicAnimation animationWithKeyPath:#"scale"];
scaleUp.autoreverses = NO;
scaleUp.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
scaleUp.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.1, 1.1, 1)];
CABasicAnimation *fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue=[NSNumber numberWithDouble:0.0];
fadeAnim.toValue=[NSNumber numberWithDouble:1.0];
// Customization for all animations:
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = 0.2f;
group.repeatCount = 1;
group.autoreverses = NO;
group.animations = #[scaleUp, fadeAnim];
// add animations to the view's layer:
[self.myButton.layer addAnimation:group forKey:#"allMyAnimations"];
} [CATransaction commit];
As you can see the code almost 3 times as long as before and the animation on the device is noticeably less 'smooth' than it was previously.
Is there any way to do this better?
Thanks in advance for your response.
EDIT: This seems to have done the trick in that the animations are smooth, but I still feel like the code for this could be a lot more succinct.
[UIView animateWithDuration:0.2f animations:^{
self.pickMeButton.alpha = 1;
CABasicAnimation* scaleUp = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleUp.duration = 0.2;
scaleUp.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1, 0.1, 1)];
scaleUp.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1)];
[self.pickMeButton.layer addAnimation:scaleUp forKey:nil];
}completion:^(BOOL finished) {
CABasicAnimation* scaleDown = [CABasicAnimation animationWithKeyPath:#"transform"];
scaleDown.duration = 0.1;
scaleDown.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1)];
scaleDown.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
[self.pickMeButton.layer addAnimation:scaleDown forKey:nil];
}];
I don't know why you want to do it with CABasicAnimation for scale. You can do it like you mention at the top of your question. Set a new value for the view's width and height constraint constant values and then use [myView layoutIfNeeded] inside animateWithDuration. If the view doesn't have height and width constraints, but has constants to the top and bottom and/or left and right edges of the superview, change those values instead.
I am trying to make an animation where two layers move to the side, scale down, and rotate a little bit in 3D, all at the same time (then move back with the layer previously at the bottom not on top). I tried several methods, but none seem to work.
I have the 3d transform animation like so:
perspectiveTransformLeft = CATransform3DIdentity;
perspectiveTransformLeft.m34 = 1.0 / 500;
perspectiveTransformLeft = CATransform3DRotate(perspectiveTransformLeft, 35.0f * M_PI / 360.0f, 0.0f, 1.0f, 0.0f);
I've tried adding a scale transform that didn't work:
perspectiveTransformLeft = CATransform3DMakeScale(0.75, 0.75, 1);
I've tried to scale the layer in an animation block, but that didn't work either:
[UIView animateWithDuration:1.0f
delay:0.0f
options: UIViewAnimationOptionCurveEaseInOut
animations:^{
endingLayer.frame = CGRectMake(20.0f, 0.0f, 724.0f, 538.0f);
switchViewBottom.layer.transform = perspectiveTransformRight;
}
completion:^(BOOL finished){
[delegate switchAnimationFinished];
}
];
I am at a loss. Can someone help me?
Do some reading on CAAnimationGroup and use CABasicAnimations instead.
That should held you achieve what you're after. I'll search for an example in my code (I previously used it) if you'll have issues implementing it.
Edit: Here's some code
typedef void (^animationCompletionBlock)(void);
typedef void (^animationStartedBlock)(void);
- (void)addAnimations:(NSArray *)animations withDuration:(CGFloat)animationDuration onView:(UIView *)aView {
animationStartedBlock startBlock = ^void(void) {
// Additional Animation start code here
};
animationCompletionBlock endBlock = ^void(void) {
// Additional animation completed code here
};
CAAnimationGroup *group = [CAAnimationGroup animation];
group.fillMode = kCAFillModeForwards;
group.removedOnCompletion = NO;
group.duration = animationDuration;
[group setAnimations:animations];
group.delegate = self;
[group setValue:startBlock forKey:#"animationStartedBlock"];
[group setValue:endBlock forKey:#"animationCompletionBlock"];
[aView.layer addAnimation:group forKey:#"yourAnimationName"];
}
This will have your completion blocks called in your delegate
// Animation Delegate
- (void)animationDidStart:(CAAnimation *)anim {
animationStartedBlock animationStartedBlock = [anim valueForKey:#"animationStartedBlock"];
if (animationStartedBlock) {
animationStartedBlock();
}
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
animationCompletionBlock animationCompleteBlock = [theAnimation valueForKey:#"animationCompletionBlock"];
if (animationCompleteBlock) {
animationCompleteBlock();
}
}
How you create the animations and add them to an array to pass to this method is up to you, depending on what animations you want.
This is an example for two scale / fade animations:
// Scale
- (CABasicAnimation *)scaleAnimationForImageView:(UIImageView *)imageView withDuration:(CGFloat)duration {
CGRect imageFrame = imageView.frame;
CABasicAnimation *resizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
[resizeAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(40.0f, imageFrame.size.height * (40.0f / imageFrame.size.width))]];
resizeAnimation.fillMode = kCAFillModeForwards;
resizeAnimation.duration = duration;
resizeAnimation.removedOnCompletion = NO;
return resizeAnimation;
}
// Fade
- (CABasicAnimation *)fadeAnimationWithFinalOpacity:(CGFloat)opacity withDuration:(CGFloat)duration {
CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
[fadeOutAnimation setToValue:[NSNumber numberWithFloat:opacity]];
fadeOutAnimation.fillMode = kCAFillModeForwards;
fadeOutAnimation.removedOnCompletion = NO;
fadeOutAnimation.duration = duration;
return fadeOutAnimation;
}