CAShapeLayer LineCap not proper at end of CABasic Animation - iphone

I have implemented a basic line drawing animation for a progress bar.The line has a round cap edge.When I animated the line drawing the beginning of line is having a rounded cap but in the end its a square. below is the code I am using am i missing anything??
CAShape Layer Code :
CAShapeLayer *lineLayer = [[CAShapeLayer alloc] init];
[lineLayer setFrame: CGRectMake(0,
0,
wifisyncView.popUpView.frame.size.width,
wifisyncView.popUpView.frame.size.height)];
[lineLayer setPath: [linePath CGPath]];
lineLayer.lineWidth = 9.0f;
lineLayer.strokeEnd = 9.0f;
lineLayer.fillColor = [[UIColor orangeColor] CGColor];
lineLayer.lineCap = kCALineCapRound;
[lineLayer setStrokeColor:[[UIColor orangeColor] CGColor]];
[lineLayer setMasksToBounds:YES];
lineLayer.cornerRadius = 30.0f;
CABASIC Animation code :
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 5 seconds "
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = NO;// Remain stroked after the animation..
drawAnimation.fillMode = kCAFillModeForwards;
// 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:1.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
drawAnimation.delegate = self;
// Add the animation to the circle
[lineLayer addAnimation:drawAnimation forKey:#"drawLineAnimation"];
Image Attached :

I dont find any mistakes in your code. I created a new project and pasted your code to viewDidLoad and is working fine. Code for your reference.
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(100, 100)];
[linePath addLineToPoint:CGPointMake(500, 100)];
CAShapeLayer *lineLayer = [[CAShapeLayer alloc] init];
[lineLayer setFrame:self.view.bounds];
[lineLayer setPath: [linePath CGPath]];
lineLayer.lineWidth = 9.0f;
lineLayer.strokeEnd = 9.0f;
lineLayer.fillColor = [[UIColor orangeColor] CGColor];
lineLayer.lineCap = kCALineCapRound;
[lineLayer setStrokeColor:[[UIColor orangeColor] CGColor]];
[lineLayer setMasksToBounds:YES];
lineLayer.cornerRadius = 30.0f;
[self.view.layer addSublayer:lineLayer];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 5 seconds "
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = NO;// Remain stroked after the animation..
drawAnimation.fillMode = kCAFillModeForwards;
// 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:1.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
drawAnimation.delegate = self;
// Add the animation to the circle
[lineLayer addAnimation:drawAnimation forKey:#"drawLineAnimation"];
I think you should try changing
[lineLayer setFrame: CGRectMake(0,
0,
wifisyncView.popUpView.frame.size.width,
wifisyncView.popUpView.frame.size.height)];
to
[lineLayer setFrame: CGRectMake(0,
0,
wifisyncView.popUpView.bounds.size.width,
wifisyncView.popUpView.bounds.size.height)];

Related

CAShapeLayer and animation position issue

I am making an iPhone app that draws a circle on the screen. I use a timer (every 5 second), so that I can 'grow' the circle, then wait 5 seconds then grow again. Now I am having trouble when animating the increase in size of the circle (I use paths and a CAShapeLayer layer). The size (from and to) are fine with the animation, its just when it starts the circle moves to the upper left hand side and grows from there. Any help or suggestions would be great. Thanks
The class where this is implemented is a UIControl, which is added to a UIView.
//Called in init method
(void) initalPop {
//Circle
self.bubble = [CAShapeLayer layer];
self.bubble.bounds = self.bounds;
self.bubble.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
self.bubble.fillColor = [
[UIColor greenColor] CGColor
];
self.bubble.strokeColor = [
[UIColor greenColor] CGColor
];
self.bubble.lineWidth = 4.0;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, nil, self.bounds);
self.bubble.path = path;
[self.layer addSublayer: self.bubble];
}
//Called when timer goes off
- (void) StageGrow {
CGFloat growSize = 50.0;
//Change the size of us and center the circle (but dont want to animate this
[CATransaction begin];
[CATransaction setValue: (id) kCFBooleanTrue forKey: kCATransactionDisableActions];
self.bounds = CGRectMake(0, 0, self.bounds.size.width + growSize, self.bounds.size.height + growSize);
self.bubble.position = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[CATransaction commit];
[self ActualGrowCircle];
} - (void) ActualGrowCircle {
CGMutablePathRef oldPath = CGPathCreateMutable();
CGPathAddEllipseInRect(oldPath, nil, self.bubble.bounds);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddEllipseInRect(path, nil, self.bounds);
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath: #"path"];
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1;
animation.delegate = self;
animation.autoreverses = NO;
animation.fromValue = (__bridge id) oldPath;
animation.toValue = (__bridge id) path;
self.bubble.bounds = self.bounds;
self.bubble.path = path;
[self.bubble addAnimation: animation forKey: #"animatePath"];
}
Let's say you're growing the bubble from size (100, 100) to size (150, 150).
You create oldPath as an ellipse in the rectangle (0, 0, 100, 100).
You create path as an ellipse in the rectangle (0, 0, 150, 150).
You set the bounds of the bubble layer to (0, 0, 150, 150), and you don't animate this change.
That means that oldPath will appear aligned to the top and left edges of the bubble layer.
One way to fix this is to create oldPath in a rectangle that is centered in the bubble's new bounds:
CGRect oldBubbleRect = CGRectInset(self.bounds, growSize / 2, growSize / 2);
CGPathRef oldPath = CGPathCreateWithEllipseInRect(oldBubbleRect, NULL);
By the way, you are leaking the paths you're creating. You need to call CGPathRelease on them after you're done with them.
Worked it out. In the long run I grouped the animations together and all became good (see code below). Thanks for your help Rob.
-(void)ActualGrowCircle {
CGMutablePathRef newPath = CGPathCreateMutable();
CGPathAddEllipseInRect(newPath, nil, self.bounds);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.repeatCount = 1;
animation.autoreverses = NO;
animation.fromValue = (__bridge id)self.bubble.path;
animation.toValue = (__bridge id)newPath;
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:#"bounds"];
animation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation2.repeatCount = 1;
animation2.autoreverses = NO;
animation2.fromValue = [NSValue valueWithCGRect:self.bubble.bounds];
animation2.toValue = [NSValue valueWithCGRect:self.bounds];
CAAnimationGroup *group = [CAAnimationGroup animation];
[group setAnimations:[NSArray arrayWithObjects: animation2, animation, nil]];
group.duration = 0.5;
group.delegate = self;
self.bubble.bounds = self.bounds;
self.bubble.path = newPath;
[self.bubble addAnimation:group forKey:#"animateGroup"];
CGPathRelease(newPath);
}

mak a uiimageView turn around another image

here is my question.
I would like to animate an image, I would like to make it turn around another image, but I don't know how to do that. I just want that it turns( not a rotation but a movement of turning like it is turning around a circle).how can I do this please ?
here is my code :
-(void){
CALayer *orbit1 = [CALayer layer];
orbit1.bounds = CGRectMake(0, 0, 200, 200);
orbit1.position = centre.center;
orbit1.cornerRadius = 100;
orbit1.borderColor= [UIColor redColor].CGColor;
orbit1.borderWidth = 1.5;
planet1 = [CALayer layer];
planet1.bounds = CGRectMake(0, 0, 44.0, 20.0);
planet1.position = CGPointMake(160, 25);
planet1.contents = (id)([UIImage imageNamed:#"bouletest_05.png"].CGImage);
[orbit1 addSublayer:planet1];
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim1.fromValue = [NSNumber numberWithFloat:0];
anim1.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
anim1.repeatCount = HUGE_VALF;
anim1.duration = 10.0;
[orbit1 addAnimation:anim1 forKey:#"transform"];
[self.view.layer addSublayer:orbit1];
}
and then :
-(void)creation{
imageView2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"abouffer_03.png"]];
[[self view] addSubview:imageView2];
imageView2.center=planet1.center;
}
look at the last line: imageView2.center=planet1.center; the problem is here
Check this post: http://nachbaur.com/blog/core-animation-part-4. There you'll find an example of how to animate view along arbitrary UIBezierPath.
So in your case you'll need to create a circle path around your 'anchor' image:
const CGFloat radius = 100.0f;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(center.x-radius, center.y-radius, 2*radius, 2*radius)];
and add CAKeyFrameAnimation with that path to the image view you want to move.

How to adjust drop shadow dynamically during an UIImageView rotation?

I use the following to code to add drop shadow:
letterE.layer.shadowColor = [[UIColor blackColor] CGColor];
letterE.layer.shadowOffset = CGSizeMake(2.5, 2.5);
letterE.layer.shadowRadius = 3.0;
letterE.layer.shadowOpacity = 0.95;
and the following to rotate:
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0];
rotationAnimation.duration = 5.0;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[letterE.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
During the animation, the shadow is static which looks weird:
How can I make the shadow dynamically updated during the animation?
I found this interesting so I gave it a shot. A possible solution is to build a second clear view under the main view, giving it (the bottom view) a shadow using the original view's path. Then you can apply the animation to both views. I did this with simple rectangular views but I see no reason why this can't be done with more complex paths or using 2 CALayers instead of 2 UIViews.
This is how I set up my views...
testView = [[UIView alloc] initWithFrame:CGRectMake(40, 40, 100, 100)];
testView.backgroundColor = [UIColor redColor];
//increase y origin of second view to simulate shadow offset
testViewShadow = [[UIView alloc] initWithFrame:CGRectMake(40, 50, 100, 100)];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0, 0, testView.frame.size.width, testView.frame.size.height));
testViewShadow.layer.shadowPath = path;
testViewShadow.layer.shadowOpacity = 1.0;
testViewShadow.layer.shadowOffset = CGSizeMake(0, 0);
testViewShadow.layer.shadowRadius = 10.0;
CFRelease(path);
[self.view addSubview:testViewShadow];
[self.view addSubview:testView];
..and my animation (just hooked up your CAAnimation to a button as an IBAction and applied to both views)...
- (IBAction)rotate:(id)sender{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0];
rotationAnimation.duration = 5.0;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[testView.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[testViewShadow.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
This is what the result looks like...
Any (2D) transformations should look believable if you apply them to both views.
Hope that helps!

Creating itunes store style "jump" animation

I am creating a bookmarking feature for my app, I'd like to show the user what happens in a way similar to itunes store, when you buy something it jumps to tabBar. I once watched some WWDC video that explained this, but can't remember how to do it. Any idea where I should start looking for?
You can take a snapshot of the view you want to animate, then create an image layer, then use Core Animation to animate that to the tab bar. Here's the code I use to do that:
- (void)animateSnapshotOfView:(UIView *)view toTab:(UINavigationController *)navController
{
NSUInteger targetTabIndex = [self.tabBarController.viewControllers indexOfObject:navController];
NSUInteger tabCount = [self.tabBarController.tabBar.items count];
// AFAIK there's no API (as of iOS 4) to get the frame of a tab bar item, so guesstimate using the index and the tab bar frame.
CGRect tabBarFrame = self.tabBarController.tabBar.frame;
CGPoint targetPoint = CGPointMake((targetTabIndex + 0.5) * tabBarFrame.size.width / tabCount, CGRectGetMidY(tabBarFrame));
targetPoint = [self.window convertPoint:targetPoint fromView:self.tabBarController.tabBar.superview];
UIGraphicsBeginImageContext(view.frame.size);
[view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect frame = [self.window convertRect:view.frame fromView:view.superview];
CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (id)image.CGImage;
imageLayer.opaque = NO;
imageLayer.opacity = 0;
imageLayer.frame = frame;
[self.window.layer insertSublayer:imageLayer above:self.tabBarController.view.layer];
CGMutablePathRef path = CGPathCreateMutable();
CGPoint startPoint = imageLayer.position;
CGPathMoveToPoint(path, NULL, startPoint.x, startPoint.y);
CGPathAddCurveToPoint(path,NULL,
startPoint.x + 100, startPoint.y,
targetPoint.x, targetPoint.y - 100,
targetPoint.x, targetPoint.y);
CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
positionAnimation.path = path;
CGPathRelease(path);
CABasicAnimation *sizeAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size"];
sizeAnimation.fromValue = [NSValue valueWithCGSize:imageLayer.frame.size];
sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(50, 50)];
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.fromValue = [NSNumber numberWithFloat:0.75];
opacityAnimation.toValue = [NSNumber numberWithFloat:0];
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:positionAnimation, sizeAnimation, opacityAnimation, nil];
animationGroup.duration = 1.0;
animationGroup.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animationGroup.delegate = self;
[animationGroup setValue:imageLayer forKey:#"animatedImageLayer"];
[imageLayer addAnimation:animationGroup forKey:#"animateToTab"];
}

Animate the drawing of lines in UIView

Can anybody please help me with this problem? I have the nsarray of lines and I need to write them one after the other within some UIView (I want to animate the hand drawing).
Here is the answer (found here : http://soulwithmobiletechnology.blogspot.fr/2012/07/how-to-animate-line-draw.html)
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50.0,0.0)];
[path addLineToPoint:CGPointMake(120.0, 600.0)];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.view.bounds;
pathLayer.path = path.CGPath;
pathLayer.strokeColor = [[UIColor redColor] CGColor];
pathLayer.fillColor = nil;
pathLayer.lineWidth = 2.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[self.view.layer addSublayer:pathLayer];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 2.0;
pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
[pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
Do not forget to #import <QuartzCore/QuartzCore.h>.
Some entry points that might help :
Apple documentations: Core Animation, Quartz 2D and Quartz Demo Sample code
Some Core Animation samples
this code animates an object over a set of points. can use same idea for your lines, which i assume are CGPath?
//
// animate along a set of points
//
// NSLog(#"The content of movement1PointsArray is%#",movement1PointsArray);
CGMutablePathRef touchPath1 = CGPathCreateMutable();
CGPoint touchPath1StartPoint = [[movement1PointsArray objectAtIndex:0] CGPointValue];
CGPathMoveToPoint(touchPath1,NULL,touchPath1StartPoint.x, touchPath1StartPoint.y);
for (NSInteger p = 0; p < [movement1PointsArray count]; ++p)
{
CGPoint touchesPointOnPath = [[movement1PointsArray objectAtIndex:p] CGPointValue];
CGPathAddLineToPoint(touchPath1, NULL,touchesPointOnPath.x,touchesPointOnPath.y);
}
CAKeyframeAnimation* touchPathAnimation1 = [CAKeyframeAnimation animationWithKeyPath:#"position"];
[touchPathAnimation1 setDuration: 1.0];
[touchPathAnimation1 setAutoreverses: NO];
touchPathAnimation1.removedOnCompletion = NO;
touchPathAnimation1.fillMode = kCAFillModeForwards;
[touchPathAnimation1 setPath: touchPath1];
CFRelease(touchPath1);
[animationsArray addObject:touchPathAnimation1];
[ball.layer addAnimation: touchPathAnimation1 forKey: #"position"];
i am having trouble getting a second path to animate...regardless of what i try, it only animates the last path.
You probably end up implementing the views drawRect method.
This http://howtomakeiphoneapps.com/2009/08/how-to-draw-shapes-with-core-graphics/
Should get you started