Animation for ,Both end of circle moves in clockwisedirection [closed] - ios

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have to create a circular animation in IOS where both end of the circle increses , what it actually mean is one end increaes at slow speed and other end at high speed in both clockwise direction. So as to make the complete circle.

You can try this code.
-(CAShapeLayer *) createArcWithCenter:(CGPoint)center radius:(CGFloat)radius
startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle
clockwise : (BOOL)clockwise duration :(CGFloat)duration
{
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath
bezierPathWithArcCenter:center radius:radius
startAngle:startAngle endAngle:endAngle
clockwise:clockwise].CGPath;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor greenColor].CGColor;
circle.lineWidth = 4;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = duration;
animation.removedOnCompletion = NO;
animation.fromValue = #(0);
animation.toValue = #(1);
animation.timingFunction= [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
[circle addAnimation:animation forKey:#"drawCircleAnimation"];
return circle;
}
-(void)viewDidAppear:(BOOL)animated
{
CGFloat startAngle = 0.0;
CGFloat endAngle1 = M_PI * 2.0 / 2;
CGFloat endAngle2 = M_PI * 2.0 - endAngle1;
CGPoint center = CGPointMake(50, 50);
CGFloat radius = 50;
CGFloat duration = 6.0;
UIView *circleView = [[UIView alloc]initWithFrame:CGRectMake(100, 100, radius * 2 , radius * 2)];
[self.view addSubview:circleView];
CAShapeLayer *arc1 = [self
createArcWithCenter:center radius:radius
startAngle:startAngle endAngle:endAngle1
clockwise:YES duration:duration];
arc1.strokeColor = [[UIColor redColor] CGColor];
CAShapeLayer *arc2 = [self
createArcWithCenter:center radius:radius
startAngle:startAngle endAngle:endAngle2
clockwise:NO duration:duration];
arc2.strokeColor = [[UIColor redColor] CGColor];
[circleView.layer addSublayer:arc1];
[circleView.layer addSublayer:arc2];
//circleView.layer.anchorPoint = CGPointMake(0.5, 0.5);
[UIView animateKeyframesWithDuration:duration delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
int numberOfAnimations = 8;
CGFloat durationPerAnimation = 1.0/numberOfAnimations;
for (int i = 0; i < numberOfAnimations; i++)
{
NSLog(#"%f",i*durationPerAnimation);
[UIView addKeyframeWithRelativeStartTime:(i * durationPerAnimation) relativeDuration:durationPerAnimation animations:^{
circleView.transform = CGAffineTransformRotate(circleView.transform, 3 * M_PI);
}];
}
} completion:^(BOOL finished) {
}];
}
You can vary the endAngle1 and endAngle2 to vary the angles of each arc.

Related

Animate CAGradientLayer while drawing or fill color

I am working on a circular progress view. I am adding slices or ARCs of a circle with unselected and selected colours that indicate progress of some task.
I am working in CALayers to draw Arcs and add animations. Everything is working fine for all slices with out gradient. If I add gradient in any slice / Arc it does not animate while drawing immediately.
Please help me in this issue to add animation in CAShapeLayer having CAGradientLayer.
Below is my code and my tried approaches...
NOTE: I have tried three approaches to add animation. Please check the commented code also
- (void)drawArcAnimation:(CGRect)rect {
if (self.itemIndex >= self.sliceItems.count) {
return;
}
float total = [self calculateTotal];
SliceItem *item = self.sliceItems[self.itemIndex];
UIBezierPath* path = [UIBezierPath bezierPath];
float angle = (item.itemValue/total) * 2 * M_PI;
float endAngle = self.startAngle + angle;
totalProgress += item.itemValue;
if (item.shouldGradient) {
// ***** This code will be used if we go for second approach of animation ****
UIBezierPath *rectToAnimateFrom = [UIBezierPath bezierPathWithArcCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) radius:rect.size.width/2 - self.lineWidth startAngle:self.startAngle endAngle:self.startAngle clockwise:NO];
UIBezierPath *rectToAnimateTo = [UIBezierPath bezierPathWithArcCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2) radius:rect.size.width/2 - self.lineWidth startAngle:self.startAngle endAngle:endAngle clockwise:NO];
// ***** END ****
CGMutablePathRef arc = CGPathCreateMutable();
CGPathAddArc(arc, NULL,
(rect.size.width / 2), (rect.size.height / 2),
rect.size.width/2 - self.lineWidth,
self.startAngle,
endAngle,
NO);
self.startAngle = endAngle;
CGFloat lineWidth = self.lineWidth;
CGPathRef strokedArc =
CGPathCreateCopyByStrokingPath(arc, NULL,
lineWidth,
kCGLineCapButt,
kCGLineJoinMiter, // the default
10); // 10 is default miter limit
CAShapeLayer *segment = [CAShapeLayer layer];
segment.fillColor = item.itemColor.CGColor;
segment.strokeColor = [UIColor clearColor].CGColor;
segment.path = strokedArc;
// [self.baseLayer addSublayer:segment];
CAGradientLayer *gradient = [CAGradientLayer layer];
if (totalProgress > 50) {
gradient.colors = #[(id)item.itemSecondaryColor.CGColor, (id)item.itemColor.CGColor,(id)item.itemColor.CGColor,(id)item.itemColor.CGColor];
}else {
gradient.colors = #[(id)item.itemColor.CGColor, (id)item.itemColor.CGColor, (id)item.itemColor.CGColor,(id)item.itemSecondaryColor.CGColor];
}
gradient.frame = CGPathGetBoundingBox(segment.path);
CAShapeLayer *mask = [CAShapeLayer layer];
CGAffineTransform translation = CGAffineTransformMakeTranslation(-CGRectGetMinX(gradient.frame),
-CGRectGetMinY(gradient.frame));
mask.path = CGPathCreateCopyByTransformingPath(segment.path,
&translation);
gradient.mask = mask;
[self.baseLayer addSublayer:gradient];
if (_duration == 0.0) {
self.itemIndex++;
[self drawArcAnimation:rect];
}else {
// ******* First Approach to animate *******
[CATransaction begin];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = _duration*(angle/(2 * M_PI));
drawAnimation.repeatCount = 1.0;
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[CATransaction setCompletionBlock:^{
self.itemIndex++;
[self drawArcAnimation:rect];
}];
[gradient addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
[CATransaction commit];
// ******* Second Approach to animate *******
// [CATransaction begin];
// CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
// animation.fromValue = (__bridge id _Nullable)(rectToAnimateFrom.CGPath);
// animation.toValue = (__bridge id _Nullable)(rectToAnimateTo.CGPath);
// animation.duration = 3;
// animation.repeatCount = 1;
// animation.removedOnCompletion = false;
// animation.fillMode = kCAFillModeForwards;
//
// [CATransaction setCompletionBlock:^{
// self.itemIndex++;
// [self drawArcAnimation:rect];
// }];
//
// [gradient addAnimation:animation forKey:#"fill animation"];
// [CATransaction commit];
// ******* Third Approach to animate *******
// NSArray *fromColors = #[(id)UIColor.whiteColor.CGColor, (id)UIColor.clearColor.CGColor];
// NSArray *toColors = gradient.colors;
// [gradient setColors:toColors];
//
// [CATransaction begin];
// CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:#"colors"];
// animation1.fromValue = fromColors;
// animation1.toValue = toColors;
// animation1.duration = _duration;
// animation1.removedOnCompletion = YES;
// animation1.fillMode = kCAFillModeForwards;
// animation1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
// [CATransaction setCompletionBlock:^{
// self.itemIndex++;
// [self drawArcAnimation:rect];
// }];
// [gradient addAnimation:animation1 forKey:#"animateGradient"];
// [CATransaction commit];
}
}else {
[path addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:rect.size.width/2 - self.lineWidth
startAngle:self.startAngle
endAngle:endAngle
clockwise:YES];
self.startAngle = endAngle;
CAShapeLayer *layer = [CAShapeLayer layer];
layer.path = path.CGPath;
CGRect frame = path.bounds;
frame.size.height = rect.size.height;
frame.size.width = frame.size.width * 30;
layer.strokeColor = item.itemColor.CGColor;
layer.fillColor = nil;
layer.lineWidth = self.lineWidth;
layer.strokeStart = 0;
layer.strokeEnd = 1.0;
[self.baseLayer addSublayer:layer];
if (_duration == 0.0) {
self.itemIndex++;
[self drawArcAnimation:rect];
}else {
[CATransaction begin];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = _duration*(angle/(2 * M_PI));
animation.fromValue = #(0.0);
animation.toValue = #(1.0);
[CATransaction setCompletionBlock:^{
self.itemIndex++;
[self drawArcAnimation:rect];
}];
[layer addAnimation:animation forKey:nil];
[CATransaction commit];
}
}
}
In above code you can a condition if (item.shouldGradien) and its else part. Animations are working fine in else part of CALayers but no in gradient part.
What I am doing wrong here? Any help... ???

How to Draw and animate the wifi signal pulse

i want to draw an wifi signal pulse and want to animate with that in launch screen. i tried with this code. the signal is showing in the down words but i need to show in up words. Starting from a point ti increase [like our phone wifi signal]
- (void)viewDidLoad
{
[super viewDidLoad];
[self addWavesToView];
[self addAnimationsToLayers];
}
- (void)addWavesToView
{
CGRect rect = CGRectMake(0.0f, 0.0f, 300.0f, 300.0f);
UIBezierPath *circlePath = [UIBezierPath
bezierPathWithOvalInRect:rect];
for (NSInteger i = 0; i < 10; ++i) {
CAShapeLayer *waveLayer = [CAShapeLayer layer];
waveLayer.bounds = rect;
waveLayer.position = CGPointMake(100, 100);
waveLayer.strokeColor = [[UIColor lightGrayColor] CGColor];
waveLayer.fillColor = [[UIColor clearColor] CGColor];
waveLayer.lineWidth = 5.0f;
waveLayer.path = circlePath.CGPath;
waveLayer.transform = CATransform3DMakeTranslation(0.0f, i*25, 0.0f);
waveLayer.strokeStart = 0.25- ((i+1) * 0.01f);
waveLayer.strokeEnd = 0.25+((i+1) * 0.01f);
[self.view.layer addSublayer:waveLayer];
}
}
- (void)addAnimationsToLayers
{
NSInteger timeOffset = 10;
for (CALayer *layer in self.view.layer.sublayers) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeColor"];
animation.duration = 10.0f;
// Stagger the animations so they don't begin until the
// desired time in each
animation.timeOffset = timeOffset--;
animation.toValue = (id)[[UIColor lightGrayColor] CGColor];
animation.fromValue = (id)[[UIColor darkGrayColor] CGColor];
// Repeat forever
animation.repeatCount = HUGE_VALF;
// Run it at 10x. You can adjust this to taste.
animation.speed = 10;
// Add to the layer to start the animation
[layer addAnimation:animation forKey:#"strokeColor"];
}
"show in up words", it'll take more time so we can rotate base view to 180 degree by this code inside of viewDidLoad() Function
CGFloat degreesOfRotation = 180.0;
self.view.transform = CGAffineTransformRotate(self.view.transform,
degreesOfRotation * M_PI/180.0);

Scale a circle at same center position

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
}];

iOS - Pie progress bar with rounded corners

I need to implement a progress bar according to this design :
As you can see, there is a corner radius to the bar itself.
This is how it looks now with my current code :
So, how to do that?
This is my current code:
- (void)animateProgressBarToPercent:(float)percent
{
if (percent > 1.0f) return;
int radius = 42.7f;
int strokeWidth = 7.f;
CGColorRef color = [UIColor someColor].CGColor;
int timeInSeconds = percent * 5;
CGFloat startAngle = 0;
CGFloat endAngle = percent;
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius) cornerRadius:radius].CGPath;
circle.position = CGPointMake(self.center.x - radius, self.center.y + strokeWidth);
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = color;
circle.lineWidth = strokeWidth;
circle.strokeEnd = endAngle;
[self.layer addSublayer:circle];
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = timeInSeconds;
drawAnimation.repeatCount = 1.0;
drawAnimation.removedOnCompletion = NO;
drawAnimation.fromValue = [NSNumber numberWithFloat:startAngle];
drawAnimation.toValue = [NSNumber numberWithFloat:endAngle];
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];
}
circle.lineCap = kCALineCapRound
Just set this property of your CAShapeLayer

How do I correctly position an animated CAShapeLayer

I'm animating the end stroke of a CAShapeLayer, but the position is incorrect and I can't figure out what I'm doing wrong. The frame is correct, but when I try and adjust the position I can't get it to "fill" the frame's width of the view. I've tried to adjust the _maskLine's position, but my approaches have failed thus far.
The frame of the view is: 10, 10, 50, 100
See image (the red shape layer should be "shifted" over 10 pts.
// layer setup in initWithFrame
_maskLine = [CAShapeLayer layer];
_maskLine.lineCap = kCALineCapButt;
_maskLine.fillColor = [[UIColor whiteColor] CGColor];
_maskLine.lineWidth = frame.size.width;
_maskLine.strokeEnd = 0.0;
_maskLine.frame = self.bounds;
[self.layer addSublayer:_maskLine];
self.clipsToBounds = YES;
self.layer.cornerRadius = 2.0f;
// animation method logic
UIBezierPath *progressline = [UIBezierPath bezierPath];
NSUInteger randomInt = arc4random() % (NSUInteger)self.maxMeterHeight;
self.meterHeight = (randomInt / self.maxMeterHeight);
[progressline moveToPoint:CGPointMake(CGRectGetMidX(self.frame), CGRectGetHeight(self.frame))];
[progressline addLineToPoint:CGPointMake(CGRectGetMidX(self.frame), (1 - self.meterHeight) * CGRectGetHeight(self.frame))];
[progressline setLineWidth:1.0];
[progressline setLineCapStyle:kCGLineCapSquare];
self.maskLine.path = progressline.CGPath;
self.maskLine.strokeColor = [UIColor redColor].CGColor;
self.maskLine.frame = self.bounds;
NSLog(#"mask frame: %#", NSStringFromCGRect(self.maskLine.frame));
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 0.35;
pathAnimation.autoreverses = YES;
pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pathAnimation.fromValue = #0.0f;
pathAnimation.toValue = #1.0f;
pathAnimation.cumulative = YES;
[self.maskLine addAnimation:pathAnimation forKey:#"strokeEndAnimation"];
[CATransaction commit];
self.maskLine.strokeEnd = 1.0;
The "fix" was changing these lines
[progressline moveToPoint:CGPointMake(CGRectGetMidX(self.frame), CGRectGetHeight(self.frame))];
[progressline addLineToPoint:CGPointMake(CGRectGetMidX(self.frame), (1 - self.meterHeight) * CGRectGetHeight(self.frame))];
to:
[progressline moveToPoint:CGPointMake(self.frame.size.width / 2, CGRectGetHeight(self.frame))];
[progressline addLineToPoint:CGPointMake(self.frame.size.width / 2, (1 - self.meterHeight) * CGRectGetHeight(self.frame))];

Resources