1

I need to rotate a UIView indefinitely. However, I need to be able to stop it per a method, as I'm trying to implement something like a custom UIActivityIndicatorView. The view also needs to animate back to its starting rotation (0 degrees). The problem with all the approaches I've been trying to implement so far is that I'm either unable to stop it manually (until the duration ends), the animation isn't smooth or my view doesn't return back to its starting position.

Basically, what I need is an animation to rotate my view forever. Once I call a method to stop it, it should return to its starting point and stop.

I've tried a few modified answers here, but no success.

Community
  • 1
  • 1
Martin Herman
  • 888
  • 13
  • 34

2 Answers2

3

OK, just built this to check if it would work.

In Swift (because I'm learning) I've done this...

import UIKit

class ViewController: UIViewController {

    @IBOutlet var rotatingView : UIView
    var rotating = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func start(sender : AnyObject) {
        rotating = true

        rotateOnce()
    }

    func rotateOnce() {
        UIView.animateWithDuration(1.0,
            delay: 0.0,
            options: .CurveLinear,
            animations: {self.rotatingView.transform = CGAffineTransformRotate(self.rotatingView.transform, 3.1415926)},
            completion: {finished in self.rotateAgain()})
    }

    func rotateAgain() {
        UIView.animateWithDuration(1.0,
            delay: 0.0,
            options: .CurveLinear,
            animations: {self.rotatingView.transform = CGAffineTransformRotate(self.rotatingView.transform, 3.1415926)},
            completion: {finished in if self.rotating { self.rotateOnce() }})
    }

    @IBAction func stop(sender : AnyObject) {
        rotating = false
    }
}

Essentially, each single rotation is one animation. Then in the completion block I inspect a boolean rotating. If rotating == true then I run the rotation again. and again. and again.

When rotating == false then I just don't run the animation again from the completion block.

This ensures that the last animation gets to its end before actually stopping the animation.

Objective-C version

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIView *rotatingView;
@property (nonatomic, assign) BOOL rotating;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)startRotating:(id)sender {
    self.rotating = YES;
    [self firstRotation];
}

- (IBAction)stopRotating:(id)sender {
    self.rotating = NO;
}

- (void)firstRotation
{
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         self.rotatingView.transform = CGAffineTransformRotate(self.rotatingView.transform, M_PI);
                     }
                     completion:^(BOOL finished) {
                         [self secondRotation];
                     }];
}

- (void)secondRotation
{
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         self.rotatingView.transform = CGAffineTransformRotate(self.rotatingView.transform, M_PI);
                     }
                     completion:^(BOOL finished) {
                         if (self.rotating) {
                             [self firstRotation];
                         }
                     }];
}

@end
Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • did you actually try this @Frogmeister ? Because I translated it to Obj-C (not Swift), and what it does is only spin the view partially left and then right, until I call it to stop... – Martin Herman Jun 19 '14 at 16:06
  • Yep. I wrote it in swift but can rewrite it in objc if you'd like. This is code that is copy pasted from my working project. – Fogmeister Jun 19 '14 at 16:47
  • Note that the second animation doesn't go to identity. The second animation takes the current transform and rotates it by PI rads. If you do the second one back to identity then you will get your back and forth movement. – Fogmeister Jun 19 '14 at 16:51
  • @MartinHerman ok, edited with the Objective-C version and I've also run this and it works. – Fogmeister Jun 19 '14 at 17:16
  • thats the same code I translated myself :/. It still doesn't work however. The view only spins back and forth a few degrees (50 or so). – Martin Herman Jun 19 '14 at 18:29
  • the only difference between the code you provided and mine is that I use a UIImageView instead of a UIView... Other than that its the same. – Martin Herman Jun 19 '14 at 21:56
  • @MartinHerman without seeing your code I can't help any further. The code I have put here works. I have run it, that's why I put it here. Unless I can see your own code I can't comment further. – Fogmeister Jun 20 '14 at 17:35
  • After you posted the Objective-C version of the code, I used that one instead of my own translation. So the codes match 100%. I created a video to demonstrate what it does http://youtu.be/BOLqjjcD0uk (ignore the warning about the bad request). As you can see the triangle spins left right. – Martin Herman Jun 21 '14 at 09:42
  • @MartinHerman without being able to see your code I can't comment. But it looks like you have the animation going to PI rads and then to identity. If can only comment if I can see your own code though. If you used the code I put them you shouldn't be having this problem. Can you share just your animation class in gist. Like I've said before. Without seeing your code there is nothing I can do. – Fogmeister Jun 21 '14 at 19:11
  • @MartinHerman well I've had a look and there isn't anything that looks like it would cause the problem you are having. You can see though that my code works just by looking at what it is doing. Have you tried to eliminate everything else that could be causing this. I'd recommend creating a new project and getting it running on there. My code works. It must be something else in the project that is causing this to not work. – Fogmeister Jun 23 '14 at 10:59
1

Add this code where ever you want to perform your animation

In Objective-C

-(BOOL)rotateAnimation:(BOOL)check
{
    if (check)
    {
        [UIView animateWithDuration:0.5f animations:^{

            [ANIMATING_VIEW_OUTLET setAlpha:0.0f];
        }completion:^(BOOL finished){
            [ANIMATING_VIEW_OUTLET.layer removeAllAnimations];
            [self.view sendSubviewToBack:ANIMATING_VIEW_OUTLET];
        }];
        return NO;
    }
    else
    {
        [self.view bringSubviewToFront:ANIMATING_VIEW_OUTLET];

        [UIView animateWithDuration:0.5f animations:^{

            [ANIMATING_VIEW_OUTLET setAlpha:1.0f];
        }];

        CABasicAnimation* animationFull = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
        animationFull.fromValue = @0.0f;
        animationFull.toValue = @(2*M_PI);
        animationFull.duration = 0.75f;             // this might be too fast
        animationFull.repeatCount = HUGE_VALF;     // HUGE_VALF is defined in math.h so import it
        [ANIMATING_VIEW_OUTLET.layer addAnimation:animationFull forKey:@"rotation"];

        return YES;
    }
}

To invoke/start animation

[self rotateAnimation:YES];

To stop animation

[self rotateAnimation:YES];

In Swift 2.2

func rotateAnimation(check : Bool) -> Bool {

        if check == true {
            UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
                kAnimatingViewOutlet.alpha = 0
                }, completion: { (finished) in
                    kAnimatingViewOutlet.layer.removeAllAnimations()
            })
            return false
        }
        else if check == false {

            UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
                kAnimatingViewOutlet.alpha = 1
                }, completion: { (finished) in

            })
            let animationFull : CABasicAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
            animationFull.fromValue     = 0
            animationFull.toValue       = 2*M_PI
            animationFull.duration      = 0.75 // this might be too fast
            animationFull.repeatCount   = Float.infinity
            kAnimatingViewOutlet.layer.addAnimation(animationFull, forKey: "rotation")
            return true
        else {
            print("check value is nil")
        }
    }

To invoke/start animation

rotateAnimation(true)

To stop animation

rotateAnimation(false)
aashish tamsya
  • 4,903
  • 3
  • 23
  • 34
  • 1
    Thanks I think for common use of the code you don't need to mention 'sendSubviewToBack' and `bringSubviewToFront` here. You should remove that rest is good. – Vaibhav Saran Apr 12 '16 at 09:16