I am stuck at animating the uiLabel in my view.
Actually I just want to animate my label/Text on a circular path. I have searched a lot over internet but couldn't find any good tutorial or example that is doing something like my problem. I have done CAShapeLayer (circle) increasing from 0 to its maximum value....
Similarly I want that my label/text start moving from initial point in circular path and come back to its initial point after completing one circle.
Hope I have explained my problem quite precisely ... still if there is anything missing or non-understandable. please ask me.
Thanks in anticipation.
Hi iOmi i just google it and i found Best Question similar like your This
Bellow it's a jin's answer Hope its helps you
CAShapeLayer* circle = [[CAShapeLayer alloc] init];
CGMutablePathRef path = CGPathCreateMutable();
CGRect textRect = CGRectMake(self.view.bounds.size.width/4, (self.view.bounds.size.height-self.view.bounds.size.width/2)/2, self.view.bounds.size.width/2, self.bounds.size.width/2);
float midX = CGRectGetMidX(textRect);
float midY = CGRectGetMidY(textRect);
CGAffineTransform t = CGAffineTransformConcat(
CGAffineTransformConcat(
CGAffineTransformMakeTranslation(-midX, -midY),
CGAffineTransformMakeRotation(-1.57079633/0.99)),
CGAffineTransformMakeTranslation(midX, midY));
CGPathAddEllipseInRect(path, &t, textRect);
circle.path = path;
circle.frame = self.bounds;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 60.0f;
[self.layer addSublayer:circle];
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 15.0f;
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat:1.0f];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[circle addAnimation:animation forKey:#"strokeEnd"];
[circle release];
UILabel* label = [[UILabel alloc] init];
label.text = #"Test Text";
label.font = [UIFont systemFontOfSize:20.0f];
label.center = CGPathGetCurrentPoint(path);
label.transform = CGAffineTransformMakeRotation(1.57079633);
[label sizeToFit];
[self.layer addSublayer:label.layer];
CAKeyframeAnimation* textAnimation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
textAnimation.duration = 15.0f;
textAnimation.path = path;
textAnimation.rotationMode = kCAAnimationRotateAuto;
textAnimation.calculationMode = kCAAnimationCubicPaced;
textAnimation.removedOnCompletion = NO;
[label.layer addAnimation:textAnimation forKey:#"position"];
i just create a demo of it screen shot is:-
HERE IS DEMO LINK
http://www.sendspace.com/file/dq5i1e
Related
I want to approach the following scenario:
If I draw a rectangle using CGBezierPath and CAShapeLayer like the following:
CAShapeLayer *layerX = [CAShapeLayer layer];
layerX.path = path.CGPath;
layerX.lineWidth = 3;
layerX.strokeColor = [[UIColor whiteColor] CGColor];
layerX.fillColor = [[UIColor clearColor] CGColor];
[self.view.layer addSublayer: layerX];
And I want to add animation like the image I attached, this cleared area keeps moving around the rectangle path so it gives visual effect of the rectangle being drawn over and over. [Snake movement in old snake games]
I tried animating using CABasicAnimation but I literally couldn't achieve anything, thanks in advance.
Try this code may be helpful.
UIBezierPath *drawablePath = [UIBezierPath bezierPathWithRect:CGRectMake(100.0, 100.0, 150.0, 100.0)];
CAShapeLayer *squareLayer = [[CAShapeLayer alloc] init];
squareLayer.path = drawablePath.CGPath;
squareLayer.strokeColor = [UIColor redColor].CGColor;
squareLayer.lineWidth = 5.f;
squareLayer.fillColor = [UIColor clearColor].CGColor;
squareLayer.strokeEnd = 1.f;
//squareLayer.strokeEnd = 0.9; // this line use draw half line but some animation issue.
[self.view.layer addSublayer:squareLayer];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.fromValue = (id)[NSNumber numberWithFloat:0.10];
drawAnimation.toValue = (id)[NSNumber numberWithFloat:1.f];
drawAnimation.duration = 5.f;
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[squareLayer addAnimation:drawAnimation forKey:#"drawRectStroke"];
This code is use to draw rectangle with animation.
You have to animate both: strokeStart and strokeEnd and use a CAAnimationGroup to make them happen at the same time.
I found this link which does something similar but in swift. I think it might help.
I will try to implement it if you can't do it. But I can't right now.
Happy coding.
let drawablePath = UIBezierPath(rect: CGRect(x: 100, y: 100, width: 150, height: 150))
let squareLayer = CAShapeLayer()
squareLayer.path = drawablePath.cgPath;
squareLayer.strokeColor = UIColor.red.cgColor;
squareLayer.lineWidth = 5;
squareLayer.fillColor = UIColor.clear.cgColor;
squareLayer.strokeEnd = 1;
//squareLayer.strokeEnd = 0.9; // this line use draw half line but some animation issue.
flowerView.layer.addSublayer(squareLayer)
let drawAnimation = CABasicAnimation(keyPath: "strokeEnd")
drawAnimation.fromValue = 0.1
drawAnimation.toValue = 1.0
drawAnimation.duration = 5
drawAnimation.timingFunction = CAMediaTimingFunction(name:.linear)
squareLayer.add(drawAnimation, forKey:"drawRectStroke")
Not really an answer to the original question but here's a Swift 5 version of Dixit Akabari's answer, which was useful in itself in that it demonstrates how to animate drawing a UIBezierPath by animating strokeEnd.
I am working on scaling the circle and keep the same center position, which looks like the pulsing circle on map.
AnimationView.m
#property (nonatomic, strong) UIBezierPath *ovalPath;
- (void)drawCircle
{
UIGraphicsBeginImageContext(self.frame.size);
CGRect rect = CGRectMake(25.45, 49.25, 5.8, 5.8);
self.ovalPath = [UIBezierPath bezierPathWithOvalInRect: rect];
[self.ovalPath fill];
UIGraphicsEndImageContext();
}
- (void)bumpUpCircle
{
CAShapeLayer *pathLayer = [CAShapeLayer layer];
pathLayer.frame = self.bounds;
pathLayer.path = self.ovalPath.CGPath;
pathLayer.fillColor = [UIColor blackColor].CGColor;
pathLayer.lineWidth = 2.0f;
pathLayer.lineJoin = kCALineJoinBevel;
[self.layer addSublayer:pathLayer];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(10.0, 10.0, 10.0)];
animation.repeatCount = 1;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
animation.duration = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[pathLayer addAnimation:animation forKey:#"transform.scale"];
NSLog(#"%#",NSStringFromCGPoint(self.bounds.origin));
}
However, the scaling position is in a strange position, rather than the center of the circle.
Any ideas will be very appreciated. Thanks.
Edit:
GIF uploaded
My suggestion for you with a pulsing circle, is to use an UIView and add it as a subview in your UIViewController. In following example you get a pulsing circle (a UIView with a cornerRadius like the radius) scaling up and down in a very few lines. Include this e.g. in your UIViewController's viewDidLoad to try it out.
int radius = 100;
UIView * circleView = [[UIView alloc] init];
circleView.backgroundColor = [UIColor greenColor];
circleView.frame = CGRectMake(0, 0, radius*2, radius*2);
circleView.center = self.view.center;
circleView.layer.cornerRadius = radius;
[self.view addSubview:circleView];
[UIView animateWithDuration:0.8f
delay:0.0f
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat
animations:^{
circleView.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL fin) {
// Do nothing
}];
I have CALayer subclass. After creation of the layer I want to animate affineTransform of layer but without success. New value is set but the change is not animated. If I trigger the animation after 1 second everything works perfect.
RCLClockDialLayer *layer = [RCLClockDialLayer layer];
layer.frame = CGRectMake(center.x, center.y, 2, self.radius);
layer.center = center;
layer.radius = self.radius;
layer.width = width;
layer.backgroundColor = [UIColor blackColor].CGColor;
layer.color = [UIColor whiteColor];
layer.affineTransform = CGAffineTransformRotate(layer.affineTransform, RCLDegreesToRadians(270));
[self addSubLayer:layer];
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformRotate(transform, RCLDegreesToRadians(80));
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"affineTransform"];
animation.duration = .5;
animation.fromValue = [layer valueForKey:#"affineTransform"];
animation.toValue = [NSValue valueWithCGAffineTransform:transform];
animation.fillMode = kCAFillModeForwards;
animation.cumulative = YES;
animation.removedOnCompletion = NO;
layer.affineTransform = transform;
[layer addAnimation:animation forKey:#"affineTransform"];
This is my code. I have tried many different solutions but nothing works. Do someone know what is the reason for this?
I am developing a custom circular progress bar using UIBezierPath. I want to change the progress based on randomly generated CGFloat values.
My progress bar looks like this:
I drew the progress bar using the following code:
// Draw the arc with bezier path
int radius = 100;
CAShapeLayer *arc = [CAShapeLayer layer];
arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:M_PI endAngle:M_PI/150 clockwise:YES].CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
arc.cornerRadius = 100.0;
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.lineWidth = 6;
[self.view.layer addSublayer:arc];
// Animation of the progress bar
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = YES; // Remain stroked after the animation..
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:10.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[arc addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
// Gradient of progress bar
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = self.view.frame;
gradientLayer.colors = #[(__bridge id)[UIColor redColor].CGColor,(__bridge id)[UIColor yellowColor].CGColor,(__bridge id)[UIColor purpleColor].CGColor ];
gradientLayer.startPoint = CGPointMake(0,0.1);
gradientLayer.endPoint = CGPointMake(0.5,0.2);
[self.view.layer addSublayer:gradientLayer];
gradientLayer.mask = arc;
refreshButton = [[UIButton alloc] initWithFrame:CGRectMake(50, 370, 50, 50)];
[refreshButton addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventTouchUpInside];
[refreshButton setBackgroundImage:[UIImage imageNamed:#"refresh.png"] forState:UIControlStateNormal];
[self.view addSubview:refreshButton];
The UIButton with the green arrow generates randomly CGFloat values. So my question is how to reset the animation so that it stops at the corresponding position if it is between 0.0 and 1.0? I tried to insert the code for the animation inside the method of the button, but as a result the new arc was drawn on top of the existing one.
-(void)refresh:(id)sender{
NSLog(#"Refresh action:");
CGFloat randomValue = ( arc4random() % 256 / 256.0 );
NSLog(#"%f", randomValue);
valueLabel.text = #"";
valueLabel = [[UILabel alloc] initWithFrame:CGRectMake(150, 370, 100, 50)];
valueLabel.text = [NSString stringWithFormat: #"%.6f", randomValue];
[self.view addSubview:valueLabel];
// Draw the arc with bezier path
int radius = 100;
CAShapeLayer *arc = [CAShapeLayer layer];
arc.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 50) radius:radius startAngle:M_PI endAngle:M_PI/150 clockwise:YES].CGPath;
arc.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
arc.cornerRadius = 100.0;
arc.fillColor = [UIColor clearColor].CGColor;
arc.strokeColor = [UIColor purpleColor].CGColor;
arc.lineWidth = 6;
[self.view.layer addSublayer:arc];
// Animation of the progress bar
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 5.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
drawAnimation.removedOnCompletion = YES; // Remain stroked after the animation..
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:randomValue];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[arc addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
}
I have no clue how to re-draw the arc, so any help or idea would be greatly appreciated!
Thanks a lot.
Granit
Try re-using the sublayer or replacing it. Make arc a class variable. Then cycle through the sublayers, remove the old arc then remake it with your new random float percentage. Once arc is a class variable then you can either replace it and animate from zero or you can animate from it's current percent to a new percent.
I have a circle that I animate growing outwards and getting bigger. Unfortunately despite me setting the anchorPoint it doesnt animate from the center outwards, it seems to lock position top left. What is going on?
See this image that depicts the circles off center
Code
- (void)setLayerProperties {
rippleLayer = [[CAShapeLayer alloc] init];
rippleLayer.fillColor = [UIColor clearColor].CGColor;
rippleLayer.strokeColor = _Color.CGColor;
//rippleLayer.contentsGravity = #"center";
rippleLayer.anchorPoint = CGPointMake(0.5,0.5);
rippleLayer.bounds = self.bounds;
rippleLayer.path = bezierPath.CGPath;
[self.layer addSublayer:rippleLayer];
}
// Now animate the circle outwards
rippleLayer.opacity = 1;
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
[scale setFromValue:[NSNumber numberWithFloat:1.0f]];
[scale setToValue:[NSNumber numberWithFloat:2.0f]];
[scale setRepeatCount:1];
[scale setDuration:1.0f];
//[scale setRemovedOnCompletion:YES];
[scale setFillMode:kCAFillModeForwards];
[rippleLayer addAnimation:scale forKey:scale.keyPath];
To scale layer at it's center, you need to set layer's position.This may help you
I want to draw a Smooth animations in iOS using CAPropertyAnimations
define RADIANS(angle) ((angle) / 180.0 * M_PI)
CGPoint testCenter = CGPointMake(144.5, 230.0);
CAShapeLayer *aTestLayer = [CAShapeLayer layer];
aTestLayer.path = [UIBezierPath bezierPathWithArcCenter:**CGPointZero** radius:15.0 startAngle:RADIANS(0) endAngle:RADIANS(360) clockwise:YES].CGPath;
aTestLayer.fillColor = [UIColor clearColor].CGColor;
aTestLayer.strokeColor = [UIColor whiteColor].CGColor;
**aTestLayer.position = testCenter;**
aTestLayer.borderWidth = 2.0;
[self.view.layer addSublayer:aTestLayer];
CABasicAnimation *pulse = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
pulse.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
pulse.duration = 2;
pulse.repeatCount = HUGE_VALF;
pulse.autoreverses = YES;
pulse.fromValue = [NSNumber numberWithFloat:1.0];
pulse.toValue = [NSNumber numberWithFloat:2.0];
[aTestLayer addAnimation:pulse forKey:nil];