我试图旋转一个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度。


当前回答

霉霉4更新了David Rysanek的精彩回答:

import UIKit

extension UIView {

        func startRotating(duration: CFTimeInterval = 3, repeatCount: Float = Float.infinity, clockwise: Bool = true) {

            if self.layer.animation(forKey: "transform.rotation.z") != nil {
                return
            }

            let animation = CABasicAnimation(keyPath: "transform.rotation.z")
            let direction = clockwise ? 1.0 : -1.0
            animation.toValue = NSNumber(value: .pi * 2 * direction)
            animation.duration = duration
            animation.isCumulative = true
            animation.repeatCount = repeatCount
            self.layer.add(animation, forKey:"transform.rotation.z")
        }

        func stopRotating() {

            self.layer.removeAnimation(forKey: "transform.rotation.z")

        }   

    }
}

其他回答

如果你想要做的只是无休止地旋转图像,这很好,而且非常简单:

NSTimeInterval duration = 10.0f;
CGFloat angle = M_PI / 2.0f;
CGAffineTransform rotateTransform = CGAffineTransformRotate(imageView.transform, angle);

[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionRepeat| UIViewAnimationOptionCurveLinear animations:^{
    imageView.transform = rotateTransform;
} completion:nil];

根据我的经验,这是完美的,但要确保你的图像能够在没有任何偏移的情况下围绕其中心旋转,否则图像动画将“跳跃”一旦它到达PI。

要改变旋转的方向,改变角度的符号(angle *= -1)。

@AlexPretzlav的更新评论让我重新审视了这个问题,我意识到当我写这篇文章时,我旋转的图像是沿着垂直和水平轴镜像的,这意味着图像确实只旋转了90度,然后重置,尽管它看起来像在继续旋转。

所以,如果你的图像像我的一样,这将工作得很好,然而,如果图像不是对称的,你会注意到90度后“snap”回到原来的方向。

要旋转非对称图像,最好使用公认的答案。

这些不太优雅的解决方案之一,如下所示,将真正旋转图像,但当动画重新启动时可能会有明显的口吃:

- (void)spin
{
    NSTimeInterval duration = 0.5f;
    CGFloat angle = M_PI_2;
    CGAffineTransform rotateTransform = CGAffineTransformRotate(self.imageView.transform, angle);

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        self.imageView.transform = rotateTransform;
    } completion:^(BOOL finished) {
        [self spin];
    }];
}

你也可以像@richard-j-ross-iii建议的那样只用块来做,但你会得到一个保留循环警告,因为块正在捕获自己:

__block void(^spin)() = ^{
    NSTimeInterval duration = 0.5f;
    CGFloat angle = M_PI_2;
    CGAffineTransform rotateTransform = CGAffineTransformRotate(self.imageView.transform, angle);

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        self.imageView.transform = rotateTransform;
    } completion:^(BOOL finished) {
        spin();
    }];
};
spin();
import UIKit

class RotatingImageView: UIImageView, CAAnimationDelegate {
    
    private let rotationAnimationKey = "rotationAnimationKey"

    private var shouldStopRotating = false
    
    func startRotating(witFullRotationDuration duration: Double = 0.5, halfRotation: Bool = true) {
        
        shouldStopRotating = false
        if layer.animation(forKey: rotationAnimationKey) == nil {
        
            let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")
            rotationAnimation.fromValue = 0.0
            rotationAnimation.toValue = halfRotation ? Float.pi : Float.pi * 2
            rotationAnimation.duration = duration
            rotationAnimation.repeatCount = 1
            rotationAnimation.delegate = self
            layer.add(rotationAnimation, forKey: rotationAnimationKey)
        
        }
        
    }

    func stopRotating(immediately: Bool = false) {
        if immediately {
            if layer.animation(forKey: rotationAnimationKey) != nil {
                layer.removeAnimation(forKey: rotationAnimationKey)
            }
        } else {
            shouldStopRotating = true
        }
    }
    
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
    
        if !shouldStopRotating {
            startRotating(witFullRotationDuration: anim.duration)
        } else {
            if layer.animation(forKey: rotationAnimationKey) != nil {
                layer.removeAnimation(forKey: rotationAnimationKey)
            }
        }
        
    }
    
}

Kudos to Richard J. Ross III for the idea, but I found that his code wasn't quite what I needed. The default for options, I believe, is to give you UIViewAnimationOptionCurveEaseInOut, which doesn't look right in a continuous animation. Also, I added a check so that I could stop my animation at an even quarter turn if I needed (not infinite, but of indefinite duration), and made the acceleration ramp up during the first 90 degrees, and decelerate during the last 90 degrees (after a stop has been requested):

// an ivar for your class:
BOOL animating;

- (void)spinWithOptions:(UIViewAnimationOptions)options {
   // this spin completes 360 degrees every 2 seconds
   [UIView animateWithDuration:0.5
                         delay:0
                       options:options
                    animations:^{
                       self.imageToMove.transform = CGAffineTransformRotate(imageToMove.transform, M_PI / 2);
                    }
                    completion:^(BOOL finished) {
                       if (finished) {
                          if (animating) {
                             // if flag still set, keep spinning with constant speed
                             [self spinWithOptions: UIViewAnimationOptionCurveLinear];
                          } else if (options != UIViewAnimationOptionCurveEaseOut) {
                             // one last spin, with deceleration
                             [self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
                          }
                       }
                    }];
}

- (void)startSpin {
   if (!animating) {
      animating = YES;
      [self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
   }
}

- (void)stopSpin {
    // set the flag to stop spinning after one last 90 degree increment
    animating = NO;
}

更新

我添加了处理请求重新开始旋转(startSpin)的能力,而之前的旋转正在结束(完成)。Github上的示例项目。

我已经开发了一个闪亮的动画框架,可以节省你的时间! 使用它可以很容易地创建这个动画:

private var endlessRotater: EndlessAnimator!
override func viewDidAppear(animated: Bool) 
{
    super.viewDidAppear(animated)
    let rotationAnimation = AdditiveRotateAnimator(M_PI).to(targetView).duration(2.0).baseAnimation(.CurveLinear)
    endlessRotater = EndlessAnimator(rotationAnimation)
    endlessRotater.animate()
}

要停止这个动画,只需将nil设置为endlessRotater。

如果你感兴趣,请看看:https://github.com/hip4yes/Animatics

我发现了一个非常适合我的方法(我稍微修改了一下):iphone UIImageView旋转

#import <QuartzCore/QuartzCore.h>

- (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: M_PI * 2.0 /* full rotation*/ * rotations * duration ];
    rotationAnimation.duration = duration;
    rotationAnimation.cumulative = YES;
    rotationAnimation.repeatCount = repeat ? HUGE_VALF : 0;

    [view.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}