objective c mask animation - objective-c

I want to make an animation as below. It is like Angry Birds Game's settings wheel button.
I used images to describe my problem.
I can rotate the wheel
and I can up the image (set_bg.png).
startup position:
end position (after clicked the wheel):
as you see on startup position, I want to hide above wheel but I couldn't do. How can I do this animation?
I tried that code:
on ViewDid:
CALayer *maskLayer = [CALayer layer];
UIImage *maskImage = [UIImage imageNamed:#"set_bg.png"];
maskLayer.contents = (id)maskImage.CGImage;
maskLayer.bounds = CGRectMake(0,0,92,219);
maskLayer.frame =CGRectMake(0,-135,92,219);
btn_setting_bg.layer.mask = maskLayer;
btn_setting_bg.layer.masksToBounds = YES;
on wheel touched:
CGRect position;
CGRect oldBounds;
CGRect newBounds;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
if(isSubMenuOpened == YES){
animation.fromValue = [NSNumber numberWithFloat:2*M_PI];
animation.toValue = [NSNumber numberWithFloat: 0.0f];
position = CGRectMake(btn_setting_bg.frame.origin.x,btn_settings.frame.origin.y + 6, btn_setting_bg.frame.size.width, btn_setting_bg.frame.size.height);
newBounds = CGRectMake(0,-135,92,219);
oldBounds = CGRectMake(0,0,92,219);
isSubMenuOpened = NO;
}else{
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
position = CGRectMake(btn_setting_bg.frame.origin.x,btn_setting_bg.frame.origin.y - btn_setting_bg.frame.size.height -13 + btn_settings.frame.size.height, btn_setting_bg.frame.size.width, btn_setting_bg.frame.size.height);
oldBounds = CGRectMake(0,-135,92,219);
newBounds = CGRectMake(0,0,92,219);
isSubMenuOpened = YES;
// NSLog([NSString stringWithFormat:#"x:%f,y:%f", position.origin.x, position.origin.y]);
}
[UIView animateWithDuration:1.2
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
[btn_setting_bg setFrame:position];
//[btn_setting_bg.layer.mask setFrame:CGRectMake(0,500,92,219)];
}
completion:^(BOOL finished){
CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:#"scale.y"];
animation2.fromValue = [NSValue valueWithCGRect:oldBounds];
animation2.toValue = [NSValue valueWithCGRect:newBounds];
animation2.duration = 1.2f;
btn_setting_bg.layer.mask.frame = newBounds;
// btn_setting_bg.layer.mask.bounds = CGRectMake(0,0,92,219);
[btn_setting_bg.layer.mask addAnimation:animation2 forKey:#"scale.y"];
}];
animation.duration = 1.2f;
animation.repeatCount = 1;
[btn_settings.layer addAnimation:animation forKey:#"SpinAnimation"];

Try animating the alpha property of wheel from 0.0f to 1.0f.

I have solved problem using alpha. It is not what I exactly I want but this is also a good solution.
Thanks to #paulrehkugler and #kate-gregory
Now my code is as simple as:
CGRect position;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
if(isSubMenuOpened == YES){
animation.fromValue = [NSNumber numberWithFloat:2*M_PI];
animation.toValue = [NSNumber numberWithFloat: 0.0f];
position = CGRectMake(btn_setting_bg.frame.origin.x,btn_settings.frame.origin.y + 6, btn_setting_bg.frame.size.width, btn_setting_bg.frame.size.height);
isSubMenuOpened = NO;
}else{
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
position = CGRectMake(btn_setting_bg.frame.origin.x,btn_setting_bg.frame.origin.y - btn_setting_bg.frame.size.height -13 + btn_settings.frame.size.height, btn_setting_bg.frame.size.width, btn_setting_bg.frame.size.height);
isSubMenuOpened = YES;
}
[UIView animateWithDuration:1.2
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
[btn_setting_bg setFrame:position];
}
completion:^(BOOL finished){
}];
animation.duration = 1.2f;
animation.repeatCount = 1;
[btn_settings.layer addAnimation:animation forKey:#"SpinAnimation"];
if(isSubMenuOpened == YES){
[UIView animateWithDuration:3.5 animations:^(void) {
btn_setting_bg.alpha = 1;
}];
}else{
[UIView animateWithDuration:0.8 animations:^(void) {
btn_setting_bg.alpha = 0;
}];
}

Related

how to Zoom image using CABasicanimation

[UIView animateWithDuration:0.5 animations:^{
imageView.frame = self.view.bounds;
} completion:^(BOOL finished) {}];
how can I write this using CABasicanimation.
below is the simple implementation
-(void)StartAnmation {
[subview.layer addAnimation:[self ZoomAnimation] forKey:#"Zoom"];
}
-(CAAnimationGroup *)ZoomAnimation {
CAAnimationGroup *ZoomAnimation = [CAAnimationGroup animation];
CABasicAnimation *In = [self zoomIn];
ZoomAnimation.animations = [NSArray arrayWithObjects: In, nil];
ZoomAnimation.duration = 2.0f;
return ZoomAnimation;
}
-(CABasicAnimation *)zoomIn {
CABasicAnimation *ZoomInAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
ZoomInAnimation.beginTime = 0.0f;
ZoomInAnimation.fromValue = [NSNumber numberWithFloat:20.0];
ZoomInAnimation.toValue = [NSNumber numberWithFloat:1.0];
ZoomInAnimation.duration = 2.0f;
return ZoomInAnimation;
}
For Zoom images you can done by using UIImageView itself.
Please refer the answer Pinch To Zoom Effect on UIImageView inside scrollView

How do you set a gradient fillcolor for cashapelayer without using a mask?

How do you set a gradient fillcolor for cashapelayer?
Related question with clearer explanation:
Using Cocoa To Follow A Path With A Gradient
I need a gradient that's not a mask, but instead a gradient based on the drawing of the cashapelayer's path.
I can't use a gradient mask on top, because I'm making a route on the minimap in my game. So if the player walks over his own tracks, it should be in a different color.
I want it like this mapview's polyline:
Source: http://cdn4.raywenderlich.com/wp-content/uploads/2014/06/23_multicolor_polyline.png
I made the minimap route by:
logging all the user's different directions, then running them through a loop into bezier paths.
I appended the Bezier paths, and then put it on a cashapelayer.
Is there a way to have a multicolored in a cashapelayer?
Is there a keypath for cabasicanimation that can put a gradient?
My code is below, and some images.
[mymapview.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
[[mymapview subviews]
makeObjectsPerformSelector:#selector(removeFromSuperview)];
int i = 0;
int x = 17;
int y = 272;
int m = 16;
UIBezierPath *kpath = [UIBezierPath bezierPath]; while (i < HistDirections.count)
{
if (i > 0)
{
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(x, y)];
if ([[HistDirections objectAtIndex:i] intValue] ==1)
{
[path addLineToPoint:CGPointMake(x, y-m)];
y = y - m;
}
else if ([[HistDirections objectAtIndex:i] intValue] ==2)
{
[path addLineToPoint:CGPointMake(x-m, y)];
x = x -m;
}
else if ([[HistDirections objectAtIndex:i] intValue] ==3)
{
[path addLineToPoint:CGPointMake(x+m, y)];
x = x+m;
}
else
{
[path addLineToPoint:CGPointMake(x, y+m)];
y = y - m;
}
[kpath appendPath:path];
}
i++;
}
[CATransaction begin];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[CATransaction setCompletionBlock:^{
UIImageView *viewpulse = [[UIImageView alloc] initWithFrame:CGRectMake(x -5, y-5, 10.0, 10.0)];
viewpulse.image = [UIImage imageNamed:#"arro.png"];
viewpulse.backgroundColor = [UIColor clearColor];
if(direction == 1)
{
viewpulse.transform = CGAffineTransformMakeRotation(-M_PI/2);
}
else if (direction == 2)
{
viewpulse.transform = CGAffineTransformMakeRotation(M_PI);
}
else if (direction == 4)
{
viewpulse.transform = CGAffineTransformMakeRotation(M_PI/2);
}
[mymapview addSubview:viewpulse];
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = 0.8;
scaleAnimation.repeatCount = HUGE_VAL;
scaleAnimation.autoreverses = YES;
scaleAnimation.fromValue = [NSNumber numberWithFloat:1.6];
scaleAnimation.toValue = [NSNumber numberWithFloat:0.8];
[viewpulse.layer addAnimation:scaleAnimation forKey:#"scale"];
}];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
kpath.lineCapStyle = kCGLineCapRound;
kpath.lineCapStyle = kCGLineJoinRound;
shapeLayer.path = [kpath CGPath];
shapeLayer.strokeColor = [[UIColor colorWithRed:51/255.0f green:(51)/255.0f blue:170/255.0f alpha:1.0f] CGColor];
shapeLayer.lineWidth = 4.0;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
[mymapview.layer addSublayer:shapeLayer];
CABasicAnimation *HAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
float dur = (HistDirections.count * 0.27);
if (dur > 2)
{
dur = 2;
}
HAnimation.duration = dur;
HAnimation.repeatCount = 1.0;
HAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
HAnimation.toValue = [NSNumber numberWithFloat:1.0f];
/*
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = mymapview.frame;
gradientLayer.colors = #[(__bridge id)[UIColor blueColor].CGColor,(__bridge id)[UIColor greenColor].CGColor,(__bridge id)[UIColor yellowColor].CGColor,(__bridge id)[UIColor orangeColor].CGColor, (__bridge id)[UIColor redColor].CGColor];
gradientLayer.startPoint = CGPointMake(0,0.5);
gradientLayer.endPoint = CGPointMake(1,0.5);
[mymapview.layer addSublayer:gradientLayer];
gradientLayer.mask = shapeLayer;*/
[CATransaction commit];
Gradient mask:
Monocolor line:
Nevermind! I figured out what I can do. Since I create the layer from multiple paths, I just put the cgpaths into an array, and looped each path into a unique cashapelayer with it's own color

UILabel size change suddenly when parent UIView animate

I want to send a subview out of my super UIView with animation, It works fine but when i tried to change the size during animation any UILabel that i have in my subview suddenly become too small.
here is part of my code
-(void)pushOutScreen:(UIViewController *)pop{
[UIView animateWithDuration:1
delay:0.0
options: UIViewAnimationTransitionFlipFromLeft
animations:^{
CGRect frame = pop.view.frame;
frame.size.height = frame.size.height /4;
frame.size.width = frame.size.width /4;
frame.origin.x = -500;
frame.origin.y = 318;
pop.view.frame = frame;
}
completion:^(BOOL finished){
NSLog(#"Done!");
}];
}
note: any UIButton or UIImage that is inside my subview animated well but i have only problem with UILabel.
UIView animation is not good option for doing this, instead of it try CAKeyframeAnimation. This is sample code for scaling UIView:
- (void) scaleView:(UIView *)popView {
CAKeyframeAnimation *animation = [CAKeyframeAnimation
animationWithKeyPath:#"transform"];
animation.delegate = self;
// CATransform3DMakeScale has 3 parameter (x,y,z)
CATransform3D scale1 = CATransform3DMakeScale(1.0, 1.0, 1);
CATransform3D scale2 = CATransform3DMakeScale(0.2, 0.2, 1);
NSArray *frameValues = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:scale1],
[NSValue valueWithCATransform3D:scale2],
nil];
[animation setValues:frameValues];
NSArray *frameTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:1.0],
nil];
[animation setKeyTimes:frameTimes];
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.duration = 1.0;
[popView.layer addAnimation:animation forKey:#"popup"];
}
you can use this after you add your UIView as subView then you can call this method as scale it. for push out a sub view with this method you need to use removeFromSubView after the animation finished.
for knowing that when it finished use
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
[subView removeFromSuperview];
}
I hope it be useful!

Objective C UIImageView rotation with animation not working good

I am creating compass in a table cell so I have problem with the rotation of the image view it start all over again from the position which is the image in the resources.
this is part of my code:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
[UIView setAnimationDelegate:self];
if(cellValue.direction == 0){
cell.compass.transform = CGAffineTransformRotate(cell.compass.transform,DegreesToRadians([cellValue.fixedDirection floatValue]));
} else {
cell.compass.transform = CGAffineTransformRotate(cell.compass.transform,DegreesToRadians([cellValue.direction floatValue]));
}
[UIView commitAnimations];
cell.compass is UIimageView and I have image in it that is arrow vertical. if the direction is 0 then it should get to that degree and after it get angle from compass it goes to the else clause. So the image starst to rotate from that vertical position not from the last rotated position.
how to get the last rotated position and continue from there
Thanks!
Try to rotate view's layer with removedOnCompletion = NO instead of rotate UIView
CALayer *layer = [cell.compass layer];
NSNumber *initRotation = [layer valueForKeyPath:#"transform.rotation"];
//CATransform3D rotationTransform = CATransform3DRotate(layer.transform, angle, 0.0, 0.0, 1.0);
//layer.transform = rotationTransform;
CABasicAnimation *animation =[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.duration = 0.5;
animation.fromValue = initRotation ;
animation.toValue = [NSNumber numberWithFloat:([initRotation floatValue] + angle)];
animation.removedOnCompletion = NO;
[layer addAnimation:animation forKey:#"transform.rotation"];
I found a solution to the problem like this:
NSLog(#"old dir %d: new dir: %d", [cellValue.oldDirection intValue], [cellValue.direction intValue]);
CABasicAnimation *animation =[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.duration = 0.5;
int oldDir = [cellValue.oldDirection intValue];
int newDir = [cellValue.direction intValue];
/// in didUpdateHeading
/// int result = 0;
/// result = fixedDir - orientation;
/// place.oldDirection = place.direction;
/// place.direction = [NSNumber numberWithInt:result];
fixed dir is the direction in degree from north to the place! and orientation is the current orientation in degree from the compass or trueHeading
if (oldDir*newDir < 0 && abs(abs(oldDir) - abs(newDir)) < 180) {
oldDir = (360 + oldDir) % 360;
newDir = (360 + newDir) % 360;
}
animation.fromValue = [NSNumber numberWithFloat:DegreesToRadians(oldDir)];
animation.toValue = [NSNumber numberWithFloat:DegreesToRadians(newDir)];
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[cell.compass.layer addAnimation:animation forKey:#"transform.rotation"];
float differenceAngle = [animation.toValue floatValue] - [animation.fromValue floatValue];
CATransform3D rotationTransform = CATransform3DMakeAffineTransform(CGAffineTransformRotate(cell.compass.transform,differenceAngle));
cell.compass.layer.transform = rotationTransform;
I hope I helped :)

CGAffineTransformMakeRotation before a CABasicAnimation

I'm performing a rotation of an UIImageView in place first before performing a rotation animation:
// rotate to the left by 90 degrees
someView.transform = CGAffineTransformMakeRotation((-0.5)*M_PI);
Then calling a rotation on the view by 180 degrees... but it seems like it is rotating the image starting from the original position as if it was not initially rotated.
- (void)rotateIndicatorToAngle: (UIView *)view angle: (NSNumber *)angleInDegrees
{
CALayer *layer = view.layer;
CGFloat duration = 5.0;
CGFloat radians = [self ConvertDegreesToRadians: [angleInDegrees floatValue]];
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: radians];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseOut];
[layer addAnimation: rotationAnimation forKey: #"rotationAnimation"];
}
What gives?
rotationAnimation.cumulative = YES;
Have you tried using rotationAnimation.additive = YES; instead of cumulative?
You could use the class methods from UIView to easily animate your view:
[UIView beginAnimation: #"Rotate" context: nil];
[UIView setAnimationDuration: 5.0f];
[UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
CGFloat radians = [self ConvertDegreesToRadians: [angleInDegrees floatValue]];
view.transform = CGAffineTransformMakeRotation(radians);
[UIView commitAnimation];
That's what I'm using most of the time, no need to use layers.
UPDATE: I mean, I find it easier not to use layers in that case, no pejorative value on using layers.