Rotate a UIView infinitely until a stop method is

2019-05-31 17:51发布

问题:

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.

回答1:

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


回答2:

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)