I have a UIView and on this UIView I have 8 buttons. I need to rotate this UIVIew every time So I am using the code below:
CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((360*M_PI)/180)];
fullRotation.duration = 30;
fullRotation.repeatCount = MAXFLOAT;
[self.menuView.layer addAnimation:fullRotation forKey:#"360"];
It rotates my view properly, But I am unable to get touch on rotating buttons. Since the Parent view is rotating and the Buttons will also rotate, Touch isn't working. Thanks
Because you rotate layer. I think you can't do what you need, but try to use CGAffineTransform
CGAffineTransform transform //desired transform
[UIView animateWithDuration:time
animations:^{menuView.transform = transform;}];
and call it by timer.
Normal view touches do not work correctly during an animation. Animation only makes the views look like they are moving. Their actual coordinates don't move.
If you want to support tapping on objects you will need to roll your own tap handling code at the superview level using the presentation layer's hitTest method.
I have a sample project on github that shows how to do that:
iOS Core Animation demo
Related
So, I am fairly new to iOS programming, and have inherited a project from a former coworker. We are building an app that contains a gauge UI. When data comes in, we want to smoothly rotate our "layer" (which is a needle image) from the current angle to a new target angle. Here is what we have, which worked well with slow data:
-(void) MoveNeedleToAngle:(float) target
{
static float old_Value = 0.0;
CABasicAnimation *rotateCurrentPressureTick = [CABasicAnimation animationWithKeyPath:#"transform.rotation");
[rotateCurrentPressureTick setDelegate:self];
rotateCurrentPressureTick.fromValue = [NSSNumber numberWithFloat:old_value/57.2958];
rotateCurrentPressureTick.removedOnCompletion=NO;
rotateCurrentPressureTick.fillMode=kCAFillModeForwards;
rotateCurrentPressureTick.toValue=[NSSNumber numberWithFloat:target/57.2958];
rotateCurrentPressureTick.duration=3; // constant 3 second sweep
[imageView_Needle.layer addAnimation:rotateCurrentPressureTick forKey:#"rotateTick"];
old_Value = target;
}
The problem is we have a new data scheme in which new data can come in (and the above method called) faster, before the animation is complete. What's happening I think is that the animation is restarted from the old target to the new target, which makes it very jumpy.
So I was wondering how to modify the above function to add a continuous/restartable behavior, as follows:
Check if the current animation is in progress and
If so, figure out where the current animation angle is, and then
Cancel the current and start a new animation from the current rotation to the new target rotation
Is it possible to build that behavior into the above function?
Thanks. Sorry if the question seems uninformed, I have studied and understand the above objects/methods, but am not an expert.
Yes you can do this using your existing method, if you add this bit of magic:
- (void)removeAnimationsFromView:(UIView*)view {
CALayer *layer = view.layer.presentationLayer;
CGAffineTransform transform = layer.affineTransform;
[layer removeAllAnimations];
view.transform = transform;
}
The presentation layer encapsulates the actual state of the animation. The view itself doesn't carry the animation state properties, basically when you set an animation end state, the view acquires that state as soon as you trigger the animation. It is the presentation layer that you 'see' during the animation.
This method captures the state of the presentation layer at the exact moment you cancel the animation, and then applies that state to the view.
Now you can use this method in your animation method, which will look something like this:
-(void) MoveNeedleToAngle:(float) target{
[self removeAnimationsFromView:imageView_Needle];
id rotation = [imageView_Needle valueForKeyPath:#"layer.transform.rotation.z"];
CGFloat old_value = [rotation floatValue]*57.2958;
// static float old_value = 0.0;
CABasicAnimation *rotateCurrentPressureTick = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
[rotateCurrentPressureTick setDelegate:self];
rotateCurrentPressureTick.fromValue = [NSNumber numberWithFloat:old_value/57.2958];
rotateCurrentPressureTick.removedOnCompletion=NO;
rotateCurrentPressureTick.fillMode=kCAFillModeForwards;
rotateCurrentPressureTick.toValue=[NSNumber numberWithFloat:target/57.2958];
rotateCurrentPressureTick.duration=3; // constant 3 second sweep
[imageView_Needle.layer addAnimation:rotateCurrentPressureTick forKey:#"rotateTick"];
old_value = target;
}
(I have made minimal changes to your method: there are a few coding style changes i would also make, but they are not relevant to your problem)
By the way, I suggest you feed your method in radians, not degrees, that will mean you can remove those 57.2958 constants.
You can get the current rotation from presentation layer and just set the toValue angle. No need to keep old_value
-(void) MoveNeedleToAngle:(float) targetRadians{
CABasicAnimation *animation =[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
animation.duration=5.0;
animation.fillMode=kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
animation.removedOnCompletion=NO;
animation.fromValue = [NSNumber numberWithFloat: [[layer.presentationLayer valueForKeyPath: #"transform.rotation"] floatValue]];
animation.toValue = [NSNumber numberWithFloat:targetRadians];
// layer.transform= CATransform3DMakeRotation(rads, 0, 0, 1);
[layer addAnimation:animation forKey:#"rotate"];
}
Another way i found (commented line above) is instead of using fromValue and toValue just set the layer transform. This will produce the same animation but the presentationLayer and the model will be in sync.
I'm coding a shake image function, and the code is below:
CABasicAnimation* shake = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
shake.fromValue = [NSNumber numberWithFloat:-0.2];
shake.toValue = [NSNumber numberWithFloat:+0.2];
shake.duration = 0.1;
shake.autoreverses = YES;
shake.repeatCount = 4;
[self.imageView.layer addAnimation:shake forKey:#"imageView"];
self.imageView.alpha = 1.0;
[UIView animateWithDuration:2.0 delay:2.0 options:UIViewAnimationOptionCurveEaseIn animations:nil completion:nil];
I have two questions:
1.the code above will shake the image around its center(transform.rotation.z).But how to make the image shake around the bottom center?
2.How can I control the start and stop of this shaking animation? For example, make the image shaking until get data from server finished then stop the animation.
you could do this by modifying the image. Make it twice as tall but have the bottom just be transparent.
A similar solution that only uses code:
Put the ImageView into another view...call it imageHolderView where imageHolderView is twice as tall as the image, but it otherwise just transparent, and then shake that instead of just your image
I've been searching around, but couldn't find an answer that seems to address my specific issue. In my app, I have a custom UIView that animates indefinitely. It's a piece of seaweed, and the animation is very subtle, to make it look like it's swaying in water. I do this with CAKeyframeAnimation objects on the transform.rotation.z and position keys. These are added to a CAAnimationGroup, which is added as a layer to my UIView, like so:
animGroup = [CAAnimationGroup animation];
animGroup.fillMode = kCAFillModeForwards;
[animGroup setAnimations:[NSArray arrayWithObjects:posAnim, rotateAnim, nil]];
animGroup.duration = prpAnim.duration;
animGroup.repeatCount = prpAnim.repeat;
animGroup.delegate = self;
[animGroup setValue:self forKey:[NSString stringWithFormat:#"paper.rot.pos.%d",objID]];
[self.layer addAnimation:animGroup forKey:[NSString stringWithFormat:#"rot.pos.%d",objID]];
I want to further rotate that UIView image (piece of seaweed) when tilting the iPad without disturbing the core animation. I can do this when it's not animated by keyframe, but when I try to combine the two, it doesn't work and I can't figure it out.
I've tried animating the layer using something like this:
CATransform3D rotatePiece3D = CATransform3DMakeRotation(-(tiltRadians), 0.0, 0.0, 1.0);
seaweedPiece.layer.transform = rotatePiece3D;
But that doesn't work either - only when the animation group is turned off. It's a 2D app, so I just want it rotate around the z-axis when tilting left or right. Any ideas how to do this?
You apparently can't alter the rotation separately while it's being animated, so what I was trying to do won't work. I ended up changing the animation key to path, which allowed me to rotate the view separately using the accelerometer.
I need a a little help with my animation.
I got two buttons, the first button calls an animation and the second one calls another animation.
Now when I press the first button a code like this one down below will be called.
The Problem is that when I press the second button the animation kinda teleports to it's starting position (second animations position), is there a way to make the animations flow together from where it was interupted?
CABasicAnimation *anim1 = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
anim1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
anim1.fromValue = [NSNumber numberWithFloat:((-10*M_PI)/180)];
anim1.toValue = [NSNumber numberWithFloat:((10*M_PI)/180)];
anim1.repeatCount = HUGE_VALF;
anim1.autoreverses = YES;
anim1.duration = .5;
[head addAnimation:anim1 forKey:#"transform"];
Virtual picture:
http://i47.tinypic.com/2yopif6.jpg
The black line is the first animation, the red is the second animation, and the green is the one I'm trying to figure out. According to the picture it was interupted in the middle of the black.
Why not using the animation method instead of the UIView class - just make sure you use the UIViewAnimationOptionBeginFromCurrentState option?
Check the reference doc here: http://developer.apple.com/library/ios/#documentation/uikit/reference/uiview_class/uiview/uiview.html
EDIT
I think this tutorial will help you better: http://wangling.me/2011/06/core-animation-101-from-and-to/. What they are trying to achieve there is to bounce a ball after triggering the animation through a button. When the button is pressed a second time the ball should bounce back from the current position. There is a section where they mention they rely on 2 delegates method animationDidStart and animationDidStop to implement this desired effect.
I have a very simple UIView animation, which causes my view to "throb":
[UIView animateWithDuration:0.2 delay:0.0f options:UIViewAnimationOptionAllowUserInteraction+UIViewAnimationOptionRepeat+UIViewAnimationOptionAutoreverse animations:^{
CGAffineTransform transf = CGAffineTransformScale(self.view.transform, 1.05f, 1.05f);
[self.view setTransform:transf];
} completion:nil];
At some point the user hits a button to cancel the animation, and apply a new transform.
[self.view.layer removeAllAnimations];
[self.view setTransform:aNewTransform];
I'd like it to reset to it's original transform, but instead it's getting increased in size by 5%.
Edit: I tried adding a completion block that resets the transform to it's original position. This works, but causes the transform I run immediately after to be trampled... the completion block gets run AFTER I apply aNewTransform.
Edit 2: I found a solution, using CABasicAnimation, instead of UIView animations. I would still be interested if anybody found a solution using UIView animations... I like the block-based interface better. This also only works, because I happen to be keeping track of my scale value separate from the one applied to the view. Everything that changes the scale uses a method that also changes self.scale
My replacement animation:
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
basicAnimation.toValue = [NSNumber numberWithFloat:self.scale*1.05f];
basicAnimation.fromValue = [NSNumber numberWithFloat:self.scale];
basicAnimation.autoreverses = YES;
basicAnimation.duration = 0.2;
basicAnimation.repeatCount = HUGE_VALF;
[self.view.layer addAnimation:basicAnimation forKey:#"Throb"];
Before starting a new animation on an existing view, you can reset the view if any of these attributes were previously altered:
// first, don't forget to stop ongoing animations for the view
[theView.layer removeAllAnimations];
// if the view was hidden
theView.hidden = NO;
// if you applied a transformation e.g. translate, scale, rotate..., reset to identity
theView.transform = CGAffineTransformIdentity;
// if you changed the anchor point, this will reset it to the center of the view
theView.layer.anchorPoint = CGPointMake(0.5, 0.5);
// if you changed the alpha, this will reset it to visible
theView.alpha = 1.;
Try putting the line
[self.view setTransform:aNewTransform];
in the completion block.