How to let a view rotate forever? - iphone

Is there a way to let a view rotate forever, with an specified speed? I need that for an indicator kind of thing. I know there is this weird Lxxxxx00ff constant (don't remember it exactly) that stands for "forever".

You can use HUGE_VAL for floating value (if I remember correctly, repeatCount property for animation is a float).
To setup animation you can create CAAnimation object using +animationWithKeyPath: method:
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 3.0f;
animation.repeatCount = HUGE_VAL;
[rotView.layer addAnimation:animation forKey:#"MyAnimation"];
If I remember correctly creating this kind of rotation using just UIView animations is impossible because rotations on 360 degrees (2*M_PI radians) are optimized to no rotation at all.
Edit: Added a Swift version.
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = NSNumber(value: 0.0)
animation.toValue = NSNumber(value: 2*Double.pi)
animation.duration = 3.0
animation.repeatCount = Float.greatestFiniteMagnitude
rotView.layer.add(animation, forKey: "MyAnimation")

my solution for this is a bit hacky since it doesn't use core animation but at least it works truly forever and doesn't require you to setup multiple animation steps.
...
// runs at 25 fps
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1.0/25
target:self
selector:#selector(rotate)
userInfo:nil
repeats:YES];
[timer fire];
...
- (void)rotate {
static int rotation = 0;
// assuming one whole rotation per second
rotation += 360.0 / 25.0;
if (rotation > 360.0) {
rotation -= 360.0;
}
animatedView.transform = CGAffineTransformMakeRotation(rotation * M_PI / 180.0);
}

my bet is:
-(void)animationDidStopSelector:... {
[UIView beginAnimations:nil context:NULL];
// you can change next 2 settings to setAnimationRepeatCount and set it to CGFLOAT_MAX
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStopSelector:...)];
[UIView setAnimationDuration:...
[view setTransform: CGAffineTransformRotate(CGAffineTransformIdentity, 6.28318531)];
[UIView commitAnimations];
}
//start rotation
[self animationDidStopSelector:...];
ok better bet:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationRepeatCount: CGFLOAT_MAX];
[UIView setAnimationDuration:2.0f];
[view setTransform: CGAffineTransformMakeRotation(6.28318531)];
[UIView commitAnimations];

Related

How to programmatically rotate button by 360 Degrees in iPhone?

how the button can be rotated by 360 degree for duration of time 30 sec and after that the button stop rotation.
A 360 rotation animation is only a few lines of code with Core Animation.
CABasicAnimation *rotate =
[CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotate.byValue = #(M_PI*2); // Change to - angle for counter clockwise rotation
rotate.duration = 30.0;
[yourButton.layer addAnimation:rotate
forKey:#"myRotationAnimation"];
By using the byValue property you are doing a relative rotation of 360 degrees to whatever rotation was there before (compared to explicitly specifying the from and to values). This means that the above code will rotate the button 360 degrees even if it is already rotated. All the answers that explicitly specify an end transform are assuming that the button isn't already rotated.
The above example is as small as possible to do just what you asked for ("be rotated by 360 degree for duration of time 30 sec"). If you want to have more control you can optionally make the animation start and/or stop slowly by specifying a timing function
rotate.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
If you haven't already added QuarzCore.framework to your project you will need to do so. Also #import <QuartzCore/QuartzCore.h> in the top of your source file.
CATransform3D myRotationTransform = CATransform3DRotate(Yourbutton.layer.transform, -1, 0.0, 0.0, 1.0);
Yourbutton.layer.transform = myRotationTransform;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: -1];
animation.duration = 30;
animation.repeatCount = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[Yourbutton.layer addAnimation:animation forKey:#"MyAnimation"];
Should work as needed! don't forget to include quartz.framework!
Well I just used
Being self.buttonImageView the ImageView you need to rotate.
[UIView animateKeyframesWithDuration:0.5 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.25 animations:^{
self.buttonImageView.transform= CGAffineTransformMakeRotation(M_PI);
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.25 animations:^{
self.buttonImageView.transform= CGAffineTransformMakeRotation(-2* M_PI);
}];
} completion:^(BOOL finished) {
[self.map setCenterCoordinate:self.map.userLocation.location.coordinate animated:YES];
self.buttonImageView.transform= CGAffineTransformMakeRotation(2* M_PI);
}];
[UIView animateWithDuration:.30f animations:^{
btnGallery.transform = CGAffineTransformRotate(CGAffineTransformIdentity, -M_PI);
}];

Rotate a UIView clockwise for an angle greater than 180 degrees

I'm animating a clock arm from pointing towards 12 o'clock to the current time. If it is say, 11 o'clock, I want the arm to rotate clockwise to the 11 o'clock position. But of course if I use:
CGAffineTransform rotation = CGAffineTransformMakeRotation(2*M_PI*11/12);
[UIView animateWithDuration:3.0
animations:^{
clockArm.transform = rotation;
}];
the rotation goes counterclockwise. I tried:
CGFloat angle = 2*M_PI*11/12;
CGAffineTransform firstRotation = CGAffineTransformMakeRotation(M_PI-0.001);
CGFloat firstRotationTime = 3.0*(M_PI/angle);
CGAffineTransform secondRotation = CGAffineTransformMakeRotation(angle);
CGFloat secondRotationTime = 3.0 - firstRotationTime;
[UIView animateWithDuration:firstRotationTime
delay:0.0
options:UIViewAnimationCurveLinear
animations:^{
self.clockArm1.transform = firstRotation;
}
completion:^(BOOL finished) {
[UIView animateWithDuration:secondRotationTime
delay:0.0
options:UIViewAnimationCurveLinear
animations:^{
self.clockArm1.transform = secondRotation;
}
completion:^(BOOL finished){
}];
}];
The animation does go clockwise, but it is choppy - the animation still seems to be doing a UIViewAnimationEaseInOut for the first animation. What am I doing wrong, or is there another way to accomplish what I want?
You can use the completion block of CATransaction to set the rotation property of the view when the animation has finished. The following function worked in my test case:
- (void) rotateViewAnimated:(UIView*)view
withDuration:(CFTimeInterval)duration
byAngle:(CGFloat)angle
{
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
rotationAnimation.duration = duration;
rotationAnimation.removedOnCompletion = YES;
[CATransaction setCompletionBlock:^{
view.transform = CGAffineTransformRotate(view.transform, angle);
}];
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
}
You use it like
[self rotateViewAnimated:self.clockArm1 withDuration:3.0 byAngle:2*M_PI*11./12.];
Thanks for #Martin R 's accepted answer. I edited a bit:
I replaced this line
rotationAnimation.removedOnCompletion = YES;
with these two lines:
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
Then, in the CompletionBlock, I added one more line:
[view.layer removeAllAnimations];
So, the code finally becomes:
- (void) rotateViewAnimated:(UIView*)view
withDuration:(CFTimeInterval)duration
byAngle:(CGFloat)angle
{
[CATransaction begin];
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.byValue = [NSNumber numberWithFloat:angle];
rotationAnimation.duration = duration;
// changed by me
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
[CATransaction setCompletionBlock:^{
view.transform = CGAffineTransformRotate(view.transform, angle);
// added by me
[view.layer removeAllAnimations]; // this is important
}];
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
[CATransaction commit];
}
My references are from:
1, https://stackoverflow.com/a/3586433/2481444
2, After rotating a CALayer using CABasicAnimation the layer jumps back to it's unrotated position
As mentioned in comments, try this iphone UIImageView rotation
or UIView Infinite 360 degree rotation animation?
#import <QuartzCore/QuartzCore.h>
- (void) runSpinAnimationWithDuration:(CGFloat) duration;
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: M_PI * 2.0 /* full rotation*/ * rotations * duration ];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[myView.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}

Update UIImageView while cycle running

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGFloat coef = tempCount / 20;
while(tempCount >= 0)
{
[imgView setTransform: CGAffineTransformRotate([imgView transform], -1*coef)];
tempCount -=coef;
[NSThread sleepForTimeInterval: 0.09];
}
}
And the problem is in than, that my image on the image view updates only after cycle. I want it to update every 0.09 seconds. What can you propose? Thanx!
If you want to rotate a view with animation use animation facilities SDK provides for you. To rotate your view on full 360 degrees you will need to use core animation.
The following code will rotate your view during 3 sec:
#import <QuartzCore/QuartzCore.h>
...
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0.0f];
animation.toValue = [NSNumber numberWithFloat: 2*M_PI];
animation.duration = 3.0f;
animation.repeatCount = 1.0f;
[imgView.layer addAnimation:animation forKey:#"MyAnimation"];
Or try the following - I have not tested the code, but it should reset view's transform to identity with animation:
[UIView beginAnimations:#"Reset dial" context: nil];
[UIView setAnimationDuration: 1.0f];
[UIView setAnimationCurve: UIViewAnimationCurveEaseOut];
[imgView setTransform:CGAffineTransformIdentity];
[UIView commitAnimations];

Animate Rotating UIImageView

I want to rotate a UIImageView by roughly 10 degrees left/right but have a smooth animation, rather than a sudden turn which I see using:
player.transform = CGAffineTransformMakeRotation(angle)
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25]; // Set how long your animation goes for
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
player.transform = CGAffineTransformMakeRotation(angle); // if angle is in radians
// if you want to use degrees instead of radians add the following above your #implementation
// #define degreesToRadians(x)(x * M_PI / 180)
// and change the above code to: player.transform = CGAffineTransformMakeRotation(degreesToRadians(angle));
[UIView commitAnimations];
// The rotation code above will rotate your object to the angle and not rotate beyond that.
// If you want to rotate the object again but continue from the current angle, use this instead:
// player.transform = CGAffineTransformRotate(player.transform, degreesToRadians(angle));
I use some code like this to achieve a similar effect (though I rotate it by 360 degrees):
CABasicAnimation *rotate;
rotate = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
rotate.fromValue = [NSNumber numberWithFloat:0];
rotate.toValue = [NSNumber numberWithFloat:deg2rad(10)];
rotate.duration = 0.25;
rotate.repeatCount = 1;
[self.view.layer addAnimation:rotate forKey:#"10"];
I use code very similar to this to spin an image view.
Converted for Swift 3/4:
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.fromValue = 0
animation.toValue = Double.pi * 2.0
animation.duration = 2
animation.repeatCount = .infinity
animation.isRemovedOnCompletion = false
imageView.layer.add(animation, forKey: "spin")
i have some code rotate 360 degrees:
- (void) runSpinAnimationOnView:(UIView*)view duration:(CGFloat)duration rotations:(CGFloat)rotations repeat:(float)repeat;
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat: rotations * 2.0 /* full rotation*/ * rotations * duration ];
rotationAnimation.duration = duration;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = repeat;
[view.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
}
call it: [self runSpinAnimationOnView:imgView duration:0.1 rotations:M_PI_4 repeat:10];
And animation rotate some degrees and again:
- (void)rotateImage:(UIImageView *)image duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay
curve:(int)curve rotations:(CGFloat)rotations
{
[UIView animateWithDuration:duration
delay:delay
options:0
animations:^{
[UIView setAnimationCurve:curve];
image.transform = CGAffineTransformMakeRotation(rotations);
}
completion:^(BOOL finished){
[self rotateImage2:image duration:duration delay:delay curve:curve rotations:rotations];
;
}];
[UIView commitAnimations];
}
- (void)rotateImage2:(UIImageView *)image duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay
curve:(int)curve rotations:(CGFloat)rotations
{
[UIView animateWithDuration:duration
delay:delay
options:0
animations:^{
[UIView setAnimationCurve:curve];
image.transform = CGAffineTransformMakeRotation(-rotations);
}
completion:^(BOOL finished){
[self rotateImage:image duration:duration delay:delay curve:curve rotations:rotations];
;
}];
[UIView commitAnimations];
}
If you want to rotate a uiimageview by roughly 10 degrees left/right
call : [self rotateImage:imgView duration:0.7 delay:0.0 curve:UIViewAnimationCurveEaseIn rotations:(M_PI/18)];
similar, some degress
A modern Swift solution, using NSTimer and CGAffineTransformMakeRotation:
class Rotation: UIViewController {
var timer = NSTimer()
var rotAngle: CGFloat = 0.0
#IBOutlet weak var rotationImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
activateTimer()
}
func activateTimer() {
//(1)
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target:self, selector:#selector(Rotation.updateCounter), userInfo: nil, repeats: true)
}
func updateCounter() {
//(2)
var rotateLeft: Bool = true
if rotateLeft {
rotAngle -= 30.0
} else {
rotAngle += 30.0
}
//(3)
UIView.animateWithDuration(2.0, animations: {
self.rotationImage.transform = CGAffineTransformMakeRotation((self.rotAngle * CGFloat(M_PI)) / 180.0)
})
}
}
Things to notice:
Connect the outlet the the UIImageView
Play with the (1)timer pace, (2)animation pace and (3)rotation angle to get the desired results. This set of variables worked for me.

Animating resizing and moving UIView at the same time

I'd like to "Stretch" a UIView to the right side, meaning increase it's frame.size.width by foo pixels and at the same time decreasing it's frame.origin.x by foo pixels, using [UIView beginAnimations] syntax.
However, if I do that, when the animation begins the view immediately resizes, and then starts the animation for the origin.
CGRect currFrame = someView.frame;
currFrame.size.width += 100;
currFrame.origin.x -= 100;
[UIView beginAnimations:#"Anim1" context:nil];
someView.frame = currFrame;
[UIView setAnimationDuration:0.7];
[UIView commitAnimations];
I've also tried breaking the animation down to 2 parts but then I can't keep the right position in place.
Any help is much appreciated!
You might need to drop down to Core Animation and break this into two explicit animations:
CABasicAnimation *stetchAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale.x"];
stetchAnimation.toValue = [NSNumber numberWithDouble:(someView.frame.size.width+100)/someView.frame.size.width];
CABasicAnimation *slideAnimation = [CABasicAnimation animationWithKeyPath:#"transform.translation.x"];
slideAnimation.toValue = [NSNumber numberWithDouble:-100];
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = [NSArray arrayWithObjects:stetchAnimation,slideAnimation,nil];
animationGroup.removedOnCompletion = NO;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.duration = 0.7;
[someView.layer addAnimation:animationGroup forKey:#"animations"];