我试图旋转一个UIImageView 360度,并在网上看了几个教程。我不能让它们都工作,UIView要么停止,要么跳转到一个新位置。

我怎样才能做到这一点呢?

我最近尝试的是:

[UIView animateWithDuration:1.0
                      delay:0.0
                    options:0
                 animations:^{
                     imageToMove.transform = CGAffineTransformMakeRotation(M_PI);
                 } 
                 completion:^(BOOL finished){
                     NSLog(@"Done!");
                 }];

但如果我使用2*pi,它根本不会移动(因为它是相同的位置)。如果我尝试只做(180度),它可以工作,但如果我再次调用这个方法,它会向后旋转。

编辑:

[UIView animateWithDuration:1.0
                      delay:0.0
                    options:0
                 animations:^{
                     [UIView setAnimationRepeatCount:HUGE_VALF];
                     [UIView setAnimationBeginsFromCurrentState:YES];
                     imageToMove.transform = CGAffineTransformMakeRotation(M_PI);
                 } 
                 completion:^(BOOL finished){
                     NSLog(@"Done!");
                 }];

也不管用。它会转到180度,暂停一秒钟,然后在重新开始之前重置回0度。


当前回答

创建动画

- (CABasicAnimation *)spinAnimationWithDuration:(CGFloat)duration clockwise:(BOOL)clockwise repeat:(BOOL)repeats
{
    CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    anim.toValue = clockwise ? @(M_PI * 2.0) : @(M_PI * -2.0);
    anim.duration = duration;
    anim.cumulative = YES;
    anim.repeatCount = repeats ? CGFLOAT_MAX : 0;
    return anim;
}

将它添加到这样的视图中

CABasicAnimation *animation = [self spinAnimationWithDuration:1.0 clockwise:YES repeat:YES];
[self.spinningView.layer addAnimation:animation forKey:@"rotationAnimation"];

这个答案有什么不同?如果你的大部分函数都返回对象,而不是在这里和那里操作一些对象,你会有更干净的代码。

其他回答

使用四分之一转弯,并逐步增加转弯。

void (^block)() = ^{
    imageToMove.transform = CGAffineTransformRotate(imageToMove.transform, M_PI / 2);
}

void (^completion)(BOOL) = ^(BOOL finished){
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:0
                     animations:block
                     completion:completion];
}

completion(YES);

你也可以用UIView和block做同样类型的动画。下面是一个类扩展方法,它可以以任何角度旋转视图。

- (void)rotationWithDuration:(NSTimeInterval)duration angle:(CGFloat)angle options:(UIViewAnimationOptions)options
{
    // Repeat a quarter rotation as many times as needed to complete the full rotation
    CGFloat sign = angle > 0 ? 1 : -1;
    __block NSUInteger numberRepeats = floorf(fabsf(angle) / M_PI_2);
    CGFloat quarterDuration = duration * M_PI_2 / fabs(angle);

    CGFloat lastRotation = angle - sign * numberRepeats * M_PI_2;
    CGFloat lastDuration = duration - quarterDuration * numberRepeats;

    __block UIViewAnimationOptions startOptions = UIViewAnimationOptionBeginFromCurrentState;
    UIViewAnimationOptions endOptions = UIViewAnimationOptionBeginFromCurrentState;

    if (options & UIViewAnimationOptionCurveEaseIn || options == UIViewAnimationOptionCurveEaseInOut) {
        startOptions |= UIViewAnimationOptionCurveEaseIn;
    } else {
        startOptions |= UIViewAnimationOptionCurveLinear;
    }

    if (options & UIViewAnimationOptionCurveEaseOut || options == UIViewAnimationOptionCurveEaseInOut) {
        endOptions |= UIViewAnimationOptionCurveEaseOut;
    } else {
        endOptions |= UIViewAnimationOptionCurveLinear;
    }

    void (^lastRotationBlock)(void) = ^ {
        [UIView animateWithDuration:lastDuration 
                              delay:0 
                            options:endOptions 
                         animations:^{
                             self.transform = CGAffineTransformRotate(self.transform, lastRotation);
                         } 
                         completion:^(BOOL finished) {
                             NSLog(@"Animation completed");   
                         }
         ];
    };

    if (numberRepeats) {
        __block void (^quarterSpinningBlock)(void) = ^{ 
            [UIView animateWithDuration:quarterDuration 
                                  delay:0 
                                options:startOptions 
                             animations:^{
                                 self.transform = CGAffineTransformRotate(self.transform, M_PI_2);
                                 numberRepeats--; 
                             } 
                             completion:^(BOOL finished) {
                                 if (numberRepeats > 0) {
                                     startOptions = UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear;
                                     quarterSpinningBlock();
                                 } else {
                                     lastRotationBlock();
                                 }NSLog(@"Animation completed");   
                             }
             ];

        };

        quarterSpinningBlock();
    } else {
        lastRotationBlock();
    }
}

Swift3版本:

extension UIView {

    func startRotate() {
        let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.fromValue = 0
        rotation.toValue = NSNumber(value: M_PI * 2)
        rotation.duration = 2
        rotation.isCumulative = true
        rotation.repeatCount = FLT_MAX
        self.layer.add(rotation, forKey: "rotationAnimation")
    }

    func stopRotate() {
        self.layer.removeAnimation(forKey: "rotationAnimation")
    }
}

记住在viewWillAppear中调用startRotate,而不是在viewDidLoad中。

我在这个库中找到了很好的代码,

下面是它的代码,我做了一些小的改变,根据我的速度需要:)

UIImageView + Rotate.h

#import <Foundation/Foundation.h>

@interface UIImageView (Rotate)
- (void)rotate360WithDuration:(CGFloat)duration repeatCount:(float)repeatCount;
- (void)pauseAnimations;
- (void)resumeAnimations;
- (void)stopAllAnimations;
@end

UIImageView + Rotate.m

#import <QuartzCore/QuartzCore.h>
#import "UIImageView+Rotate.h"

@implementation UIImageView (Rotate)

- (void)rotate360WithDuration:(CGFloat)duration repeatCount:(float)repeatCount
{

    CABasicAnimation *fullRotation;
    fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    fullRotation.fromValue = [NSNumber numberWithFloat:0];
    //fullRotation.toValue = [NSNumber numberWithFloat:(2*M_PI)];
    fullRotation.toValue = [NSNumber numberWithFloat:-(2*M_PI)]; // added this minus sign as i want to rotate it to anticlockwise
    fullRotation.duration = duration;
    fullRotation.speed = 2.0f;              // Changed rotation speed
    if (repeatCount == 0)
        fullRotation.repeatCount = MAXFLOAT;
    else
        fullRotation.repeatCount = repeatCount;

    [self.layer addAnimation:fullRotation forKey:@"360"];
}

//Not using this methods :)

- (void)stopAllAnimations
{

    [self.layer removeAllAnimations];
};

- (void)pauseAnimations
{

    [self pauseLayer:self.layer];
}

- (void)resumeAnimations
{

    [self resumeLayer:self.layer];
}

- (void)pauseLayer:(CALayer *)layer
{

    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}

- (void)resumeLayer:(CALayer *)layer
{

    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}

@end

Nate上面的答案是理想的停止和开始动画,并提供了一个更好的控制。我很好奇为什么你的没用,而他的有用。我想在这里分享我的发现和一个更简单的代码版本,它可以使UIView连续动画而不停顿。

这是我用的代码,

- (void)rotateImageView
{
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        [self.imageView setTransform:CGAffineTransformRotate(self.imageView.transform, M_PI_2)];
    }completion:^(BOOL finished){
        if (finished) {
            [self rotateImageView];
        }
    }];
}

我使用“CGAffineTransformRotate”而不是“CGAffineTransformMakeRotation”,因为前者返回的结果是随着动画的进行而保存的。这将防止在动画期间视图的跳跃或重置。

另一件事是不要使用'UIViewAnimationOptionRepeat'因为在动画的结尾,在它开始重复之前,它会重置变换,使视图跳回它的原始位置。不是重复,而是递归,这样转换就永远不会重置为原始值,因为动画块实际上永远不会结束。

最后一件事是,你必须以90度(M_PI / 2)的步伐转换视图,而不是360度或180度(2*M_PI或M_PI)。因为变换是正弦和余弦值的矩阵乘法。

t' =  [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t

所以,假设你使用180度变换,cos(180)产生-1,使得视图每次都在相反的方向变换(如果你把变换的弧度值改为M_PI, Note-Nate的答案也会有这个问题)。360度转换只是要求视图保持在原来的位置,因此你根本看不到任何旋转。