I am creating a game in which the dice action is shows in a circle.When user clicks on the wheel it rotates and stop at random position.
I search and found some code which I tried to implement like:-
[UIView beginAnimations:#"RotationAnimation" context:nil];
CALayer *myLayer = diceCircle.layer;
CABasicAnimation *fullRotationAnimation;
fullRotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotationAnimation .fromValue = [NSNumber numberWithFloat:0];
// fullRotationAnimation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotationAnimation.toValue = [NSNumber numberWithFloat:angle];
fullRotationAnimation.duration = 0.5; // speed for the rotation. Smaller number is faster
fullRotationAnimation.repeatCount = 1; // number of times to spin. 1 = once
[myLayer addAnimation:fullRotationAnimation forKey:#"360"];
[UIView commitAnimations];
angle is coming from:-
-(IBAction)diceButtonClicked:(id)sender
{
float angle=arc4random()%360;
NSLog(#"%f",angle);
float rad=radians(angle);
NSLog(#"%f",rad);
[self rotateDiceMethod:rad];
}
But the wheels stop at same position everytime and also does not always animate.Please suggest me some approach or sample code for implementing the required functionality.
Thanks in advance!
It's my old code for the same task:
CGFloat randValue= arc4random() % 10;
self.angleRotate = (randValue+30);
CABasicAnimation *spinAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
spinAnimation.fromValue = [NSNumber numberWithFloat:self.angle];
spinAnimation.toValue = [NSNumber numberWithFloat:self.angle+self.angleRotate];
spinAnimation.duration = 2.0f;
spinAnimation.cumulative = YES;
spinAnimation.additive = YES;
spinAnimation.removedOnCompletion = NO;
spinAnimation.delegate = self;
spinAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseOut];
spinAnimation.fillMode = kCAFillModeForwards;
[self.arrow.layer addAnimation:spinAnimation forKey:#"spinAnimation"];
and when rotation is stopped
- (void)animationDidStop:(CAAnimation *)spinAnimation finished:(BOOL)flag{
self.angle += self.angleRotate;
while (self.angle >= M_PI*2.0) {
self.angle -= M_PI*2.0;
}
NSLog(#"angleRotate value is: %f", self.angleRotate);
NSLog(#"angle value is: %f", self.angle);
self.angleRotate = 0;
}
Related
I'm trying to make a breathing circle animation. Only breathInAnimation (grow circle) seems to animate. breathOutAnimation (shrink circle) gets called but doesn't seem to do anything. I'm guessing it immediately reverts back to the starting state but I don't understand why.
- (void)viewDidLoad
animationView = [[UIView alloc] initWithFrame:CGRectMake(self.view.frame.size.width / 2.0, self.view.frame.size.height / 2.0, 200, 200)];
animationView.backgroundColor = [UIColor blueColor];
animationView.layer.cornerRadius = 100;
animationView.center = self.view.center;
[self.view addSubview: animationView];
[self drawCircleEdge];
[self breathInAnimation];
[NSTimer timerWithTimeInterval:7.0 target:self selector:#selector(breathOutAnimation) userInfo:nil repeats:YES];
- (void)breathInAnimation
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
[scaleAnimation setValue:#"breathIn" forKey:#"id"];
scaleAnimation.duration = 4;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.1];
scaleAnimation.toValue = [NSNumber numberWithFloat:1];
[animationView.layer addAnimation:scaleAnimation forKey:#"scale"];
- (void)breathOutAnimation
CABasicAnimation *breathOut = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
breathOut.duration = 8;
breathOut.fromValue = [NSNumber numberWithFloat:1];
breathOut.toValue = [NSNumber numberWithFloat:0.1];
[animationView.layer removeAllAnimations];
[animationView.layer addAnimation: breathOut forKey:#"scale"];
I also tried using the delegate of scaleAnimation.
- (void)animationDidStop:(CABasicAnimation *)theAnimation2 finished:(BOOL)flag
This works but after the animation finished the circle goes back to the state it was after ending the first animation. Shrinking -> animation ends -> fullsize again.
I'm not sure what I'm missing here.
CAAnimations doesn't apply the transformation to your layer, it's animating on a presentation layer and then switch back to your layer when animation is finished.
You should apply your transform when you are playing the animation.
-(void)breathOutAnimation
{
CABasicAnimation *breathOut = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
breathOut.duration = 8;
breathOut.fromValue = [NSNumber numberWithFloat:1];
breathOut.toValue = [NSNumber numberWithFloat:0.1];
[animationView.layer removeAllAnimations];
[animationView.layer addAnimation: breathOut forKey:#"scale"];
animationView.layer.transform = CATransform3DMakeScale(0.1, 0.1, 0.1);
}
Don't refer CABasicAnimation returns to the original position before the next animation and Objective-C - CABasicAnimation applying changes after animation? and CABasicAnimation rotate returns to original position
i have tried.
below code does,bottom->top->goes left->back to it’s original position.
bottom->top->goes left->back to its original position.
I need bottom->top->goes left.bottom->top->goes left so on…
-(void)addUpDownAnimationForButton:(UILabel*)label
{
CABasicAnimation * bottomAnimation ;
bottomAnimation =[CABasicAnimation animationWithKeyPath:#"transform.translation.y"];
[bottomAnimation setValue:#"animation1" forKey:#"id"];
bottomAnimation.delegate = self;
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:)];
bottomAnimation.duration = 2.0;
bottomAnimation.fromValue = [NSNumber numberWithFloat:13];
bottomAnimation.toValue = [NSNumber numberWithFloat:-7];
bottomAnimation.repeatCount = 0;
bottomAnimation.fillMode = kCAFillModeForwards;
bottomAnimation.removedOnCompletion = NO;
[btnSpecialForListing.titleLabel.layer addAnimation:bottomAnimation forKey:#"transform.translation.y"];
}
- (void)animationDidStop:(CAAnimation *)theAnimation2 finished:(BOOL)flag
{
if([[theAnimation2 valueForKey:#"id"] isEqual:#"animation1"]) {
CABasicAnimation *moveAnimation;
moveAnimation=[CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
moveAnimation.duration=3.5;
moveAnimation.repeatCount=0;
moveAnimation.autoreverses=NO;
moveAnimation.fromValue=[NSNumber numberWithFloat:0];
moveAnimation.toValue=[NSNumber numberWithFloat:-400];
moveAnimation.removedOnCompletion = NO;
moveAnimation.fillMode = kCAFillModeRemoved;
[btnSpecialForListing.titleLabel.layer addAnimation:moveAnimation forKey:#"transform.translation.x"];
}
}
Any help would be greatly appreciated.
Thanks !!
Have you tried this?
moveAnimation.fillMode = kCAFillModeForwards;
moveAnimation.removedOnCompletion = NO;
EDIT
As far as I can see you can also use animateWithDuration which has a completion block.
Example would be:
CGRect rect = btnSpecialForListing.titleLabel.frame;
[UIView animateWithDuration:2.0 animations:^{
rect.origin.y = -7;
btnSpecialForListing.titleLabel.frame = rect;
} completion:^(BOOL finished){
[UIView animateWithDuration:3.5 animation:^{
rect.origin.x = -400;
btnSpecialForListing.titleLabel.frame = rect;
}];
}];
Swift 4.2/5:
moveAnimation.fillMode = .forwards
moveAnimation.isRemovedOnCompletion = false
i got some simple animation that works perfect on iOS 6, but on iOS5 its lagging and often freezes screen without any reason
i need help really, because i dont have any ideas what could be wrong here. By the way i tried to turn off each of the animations, and looks like the problem is in "transform" animation
here is the code:
-(void)drawDotAtPointAnimated:(CGPoint)p withRadius:(CGFloat)radius andColor:(UIColor *)color
{
CGRect circleFrame = CGRectMake(p.x - radius*self.scale, p.y - radius*self.scale, radius*2*self.scale, radius*2*self.scale);
CGPoint circleAnchor = CGPointMake(0.5, 0.5);
NSLog(#"animating dot with x = %f and y = %f", p.x, p.y);
NSLog(#"=====mid x = %f, mid y = %f", CGRectGetMidX(circleFrame), CGRectGetMidY(circleFrame));
NSLog(#"=====max x = %f, max y = %f", CGRectGetMaxX(circleFrame), CGRectGetMaxY(circleFrame));
self.shapeLayer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleFrame.size.width, circleFrame.size.height)];
self.shapeLayer.path = path.CGPath;
self.shapeLayer.anchorPoint = circleAnchor;
self.shapeLayer.frame = circleFrame;
self.shapeLayer.fillColor = color.CGColor;
[self.layer addSublayer:self.shapeLayer];
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.0, 0.0, 0)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(4.0, 4.0, 1.0)];
if(self.mode == GameModeOffline) {
animation.repeatCount = OFFLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
} else {
animation.repeatCount = ONLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
}
animation.duration = 1;
animation.delegate = self;
[self.shapeLayer addAnimation:animation forKey:#"transform"];
CABasicAnimation* fadeAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnim.fromValue = [NSNumber numberWithFloat:1.0];
fadeAnim.toValue = [NSNumber numberWithFloat:0.0];
fadeAnim.duration = 1.0;
if(self.mode == GameModeOffline) {
fadeAnim.repeatCount = OFFLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
} else {
fadeAnim.repeatCount = ONLINE_GAME_DOT_ANIMATION_REPEAT_COUNT;
}
[self.shapeLayer addAnimation:fadeAnim forKey:#"opacity"];
self.shapeLayer.transform = CATransform3DMakeScale(4.0, 4.0, 1);
self.shapeLayer.opacity = 0.0;
any help would be appreciated
Scale by zero at start? Don't you want to scale by {1, 1, 1} ? (ie. identity transform)
(It seems this has simply solved the problem, so I'm posting my comment as an answer).
I have two rotation animations in my CAAnimationGroup, one that starts from zero and another that repeats and autoreverses from that state:
- (void)addWobbleAnimationToView:(UIView *)view amount:(float)amount speed:(float)speed
{
NSMutableArray *anims = [NSMutableArray array];
// initial wobble
CABasicAnimation *startWobble = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
startWobble.toValue = [NSNumber numberWithFloat:-amount];
startWobble.duration = speed/2.0;
startWobble.beginTime = 0;
startWobble.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[anims addObject:startWobble];
// rest of wobble
CABasicAnimation *wobbleAnim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
wobbleAnim.fromValue = [NSNumber numberWithFloat:-amount];
wobbleAnim.toValue = [NSNumber numberWithFloat:amount];
wobbleAnim.duration = speed;
wobbleAnim.beginTime = speed/2.0;
wobbleAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
wobbleAnim.autoreverses = YES;
wobbleAnim.repeatCount = INFINITY;
[anims addObject:wobbleAnim];
CAAnimationGroup *wobbleGroup = [CAAnimationGroup animation];
wobbleGroup.duration = DBL_MAX; // this stops it from working
wobbleGroup.animations = anims;
[view.layer addAnimation:wobbleGroup forKey:#"wobble"];
}
Since CFTimeInterval is defined as a double, I try setting the duration of the animation group to DBL_MAX, but that stops the animation group from running. However, If I set it to a large number, such as 10000, it runs fine. What is the largest number I can use for a duration of a CAAnimationGroup, to ensure it runs for as near to infinity as possible?
UPDATE: It appears that if I put in a very large value such as DBL_MAX / 4.0 then it freezes for a second, then starts animating. If I put in the value DBL_MAX / 20.0 then the freeze at the beginning is a lot smaller. It seems that having such a large value for the duration is causing it to freeze up. Is there a better way of doing this other than using a very large value for the duration?
I am faced with the exact same issue right now... I hope someone proves me wrong, but the only reasonable way I see to handle this situation is by moving the first animation to a CATransaction, and chaining that with the autoreverse animation using:
[CATransaction setCompletionBlock:block];
It's not ideal, but gets the job done.
Regarding your question about the animations being paused when coming back from background, that's a classic limitation of the CoreAnimation framework, many solutions have been proposed for it. The way I solve it is by simply reseting the animations at a random point of the animation, by randomizing the timeOffset property. The user can't tell exactly what the animation state should be, since the app was in the background. Here is some code that could help (look for the //RANDOMIZE!! comment):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(startAnimating)
name:UIApplicationWillEnterForegroundNotification
object:[UIApplication sharedApplication]];
...
for (CALayer* layer in _layers)
{
// RANDOMIZE!!!
int index = arc4random()%[levels count];
int level = ...;
CGFloat xOffset = ...;
layer.position = CGPointMake(xOffset, self.bounds.size.height/5.0f + yOffset * level);
CGFloat speed = (1.5f + (arc4random() % 40)/10.f);
CGFloat duration = (int)((self.bounds.size.width - xOffset)/speed);
NSString* keyPath = #"position.x";
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath:keyPath];
anim.fromValue = #(xOffset);
anim.toValue = #(self.bounds.size.width);
anim.duration = duration;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
// RANDOMIZE!!
anim.timeOffset = arc4random() % (int) duration;
anim.repeatCount = INFINITY;
[layer removeAnimationForKey:keyPath];
[layer addAnimation:anim forKey:keyPath];
[_animatingLayers addObject:layer];
}
It is much simpler to use a single keyframe animation instead of a group of two separate animations.
- (void)addWobbleAnimationToView:(UIView *)view amount:(CGFloat)amount speed:(NSTimeInterval)speed {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.duration = 2 * speed;
animation.values = #[ #0.0f, #(-amount), #0.0f, #(amount), #0.0f ];
animation.keyTimes = #[ #0.0, #0.25, #0.5, #0.75, #1.0 ];
CAMediaTimingFunction *easeOut =[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
CAMediaTimingFunction *easeIn =[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.timingFunctions = #[ easeOut, easeIn, easeOut, easeIn ];
animation.repeatCount = HUGE_VALF;
[view.layer addAnimation:animation forKey:animation.keyPath];
}
I have some problem with CABasicAnimation. It`s similar to that post: CABasicAnimation rotate returns to original position
So, i have uiimageview that rotate in touchMove. In touchEnd invoke method that do "animation of inertia" :
-(void)animationRotation: (float)beginValue
{
CABasicAnimation *anim;
anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
anim.duration = 0.5;
anim.repeatCount = 1;
anim.fillMode = kCAFillModeForwards;
anim.fromValue = [NSNumber numberWithFloat:beginValue];
[anim setDelegate:self];
anim.toValue = [NSNumber numberWithFloat:(360*M_PI/180 + beginValue)];
[appleView.layer addAnimation:anim forKey:#"transform"];
CGAffineTransform rot = CGAffineTransformMakeRotation(360*M_PI/180 + beginValue);
appleView.transform = rot;
}
This animation works fine, but if I invoke touchBegan before animationRotation ended, angle of rotation is beginValue. I need cath current angle of rotation. As an experiment, i declare method
-(vod) animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
NSLog(#"Animation finished!");
}
and it's seems working. but I don't know how get that value of angle or CGAffineTransform for my UIImageView in animationDidStop.
It's even possible to do? Thanks.
you should use presentationLayer method to get layer properties during animation in flight.
so your code should be like this,
#define RADIANS_TO_DEGREES(__ANGLE__) ((__ANGLE__) / (float)M_PI * 180.0f)
-(void)animationRotation: (float)beginValue
{
CABasicAnimation *anim;
anim = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
anim.duration = 0.5;
anim.repeatCount = 1;
anim.fillMode = kCAFillModeForwards;
anim.fromValue = [NSNumber numberWithFloat:beginValue];
[anim setDelegate:self];
//get current layer angle during animation in flight
CALayer *currentLayer = (CALayer *)[appleView.layer presentationLayer];
float currentAngle = [(NSNumber *)[currentLayer valueForKeyPath:#"transform.rotation.z"] floatValue];
currentAngle = roundf(RADIANS_TO_DEGREES(currentAngle));
NSLog(#"current angle: %f",currentAngle);
anim.toValue = [NSNumber numberWithFloat:(360*M_PI/180 + beginValue)];
[appleView.layer addAnimation:anim forKey:#"transform"];
CGAffineTransform rot = CGAffineTransformMakeRotation(360*M_PI/180 + beginValue);
appleView.transform = rot;
}