UIView无限360度旋转动画?

Der*_*rek 244 animation core-animation objective-c uiview ios

我正在尝试旋转UIImageView360度,并在线查看了几个教程.我可以让他们都没有工作,没有UIView停止,或跳到一个新的位置.

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

我尝试过的最新事情是:

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

但是如果我使用2*pi,它根本不会移动(因为它是相同的位置).如果我尝试做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!");
                 }];
Run Code Online (Sandbox Code Playgroud)

也不起作用.它会转到180度数,暂停一瞬间,然后在重新0开始之前重新设置为度数.

Der*_*rek 333

找到一个方法(我修改了一下),对我来说非常有效: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"];
}
Run Code Online (Sandbox Code Playgroud)

  • #import <QuartzCore/QuartzCore.h> (25认同)
  • 这可能有助于某些人,**[view.layer removeAllAnimations];**在需要时停止动画. (12认同)
  • 这是适用于iOS 3.0及以下版本的正确代码,但对于较新的程序员和新项目,Apple现在警告用户远离Docs和@Nate代码中的这些方法使用Apple现在更喜欢的基于块的动画 (9认同)
  • 添加QuartzCore.framework项目 - >构建阶段 - >链接二进制文件 (3认同)
  • 累积+1.这是正确的方法. (2认同)

Nat*_*ate 110

感谢Richard J. Ross III的想法,但我发现他的代码并不是我所需要的.options我相信默认值是给你UIViewAnimationOptionCurveEaseInOut,在连续动画中看起来不正确.此外,我添加了一个检查,以便我可以在需要时(不是无限,但是无限期)在偶数四分之一圈停止我的动画,并使加速度在前90度加速,并在最后90度减速(在要求停止后):

// 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;
}
Run Code Online (Sandbox Code Playgroud)

更新

我添加了处理再次开始旋转的请求的能力(startSpin),而之前的旋转正在逐渐减少(完成).Github上的示例项目.

  • 使用此方法,我注意到每个PI / 2角度(90度)中的动画都有短暂的暂停,并且使用CABasicAnimation所选择的答案比CPU使用率略有增加。CABasicAnimation方法可产生完美流畅的动画。 (2认同)

Kád*_*ádi 77

在Swift中,您可以使用以下代码进行无限循环:

斯威夫特4

extension UIView {
    private static let kRotationAnimationKey = "rotationanimationkey"

    func rotate(duration: Double = 1) {
        if layer.animation(forKey: UIView.kRotationAnimationKey) == nil {
            let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")

            rotationAnimation.fromValue = 0.0
            rotationAnimation.toValue = Float.pi * 2.0
            rotationAnimation.duration = duration
            rotationAnimation.repeatCount = Float.infinity

            layer.add(rotationAnimation, forKey: UIView.kRotationAnimationKey)
        }
    }

    func stopRotating() {
        if layer.animation(forKey: UIView.kRotationAnimationKey) != nil {
            layer.removeAnimation(forKey: UIView.kRotationAnimationKey)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特3

let kRotationAnimationKey = "com.myapplication.rotationanimationkey" // Any key

func rotateView(view: UIView, duration: Double = 1) {
    if view.layer.animationForKey(kRotationAnimationKey) == nil {
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")

        rotationAnimation.fromValue = 0.0
        rotationAnimation.toValue = Float(M_PI * 2.0)
        rotationAnimation.duration = duration
        rotationAnimation.repeatCount = Float.infinity

        view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
    }
}
Run Code Online (Sandbox Code Playgroud)

停止就像:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}
Run Code Online (Sandbox Code Playgroud)


ram*_*ram 66

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];
        }
    }];
}
Run Code Online (Sandbox Code Playgroud)

我使用'CGAffineTransformRotate'而不是'CGA​​ffineTransformMakeRotation',因为前者返回在动画进行时保存的结果.这将防止在动画期间跳转或重置视图.

另一件事是不使用'UIViewAnimationOptionRepeat',因为在动画开始重复之前,它会重置变换,使视图跳回到原始位置.您可以递归而不是重复,以便永远不会将变换重置为原始值,因为动画块实际上永远不会结束.

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

t' =  [ cos(angle) sin(angle) -sin(angle) cos(angle) 0 0 ] * t
Run Code Online (Sandbox Code Playgroud)

所以说,如果你用180度的转变,180个收益率-1使得每次在相反方向视图变换余弦(注意,Nate的回答也有这个问题,如果你改变转型的弧度值M_PI).360度转换只是要求视图保持原样,因此您根本看不到任何旋转.

  • @nik但是在那里设置了断点后,我发现不存在堆栈溢出,因为完成块被系统调用,到那时`rotateImageView`已经完成了.所以从那个意义上说它并不是真正的递归. (2认同)

lev*_*ker 21

如果您想要做的就是无休止地旋转图像,这非常有效,并且非常简单:

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];
Run Code Online (Sandbox Code Playgroud)

根据我的经验,这可以完美地工作,但要确保您的图像能够在没有任何偏移的情况下围绕其中心旋转,或者图像动画一旦进入PI就会"跳跃".

要更改旋转方向,请更改angle(angle *= -1)的符号.

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

因此,如果您的图像与我的图像相似,则效果很好,但是,如果图像不对称,您会注意到90度后"快照"回到原始方向.

要旋转非对称图像,最好使用接受的答案.

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

- (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];
    }];
}
Run Code Online (Sandbox Code Playgroud)

正如@ 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();
Run Code Online (Sandbox Code Playgroud)


Kev*_*OUX 20

我从已检查的解决方案中对Swift Extension的贡献:

Swift 4.0

extension UIView{
    func rotate() {
        let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.toValue = NSNumber(value: Double.pi * 2)
        rotation.duration = 1
        rotation.isCumulative = true
        rotation.repeatCount = Float.greatestFiniteMagnitude
        self.layer.add(rotation, forKey: "rotationAnimation")
    }
}
Run Code Online (Sandbox Code Playgroud)

不推荐:

extension UIView{
    func rotate() {
        let rotation : CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.toValue = NSNumber(double: M_PI * 2)
        rotation.duration = 1
        rotation.cumulative = true
        rotation.repeatCount = FLT_MAX
        self.layer.addAnimation(rotation, forKey: "rotationAnimation")
    }
}
Run Code Online (Sandbox Code Playgroud)


Geo*_*f H 12

David Rysanek的精彩回答更新为Swift 4:

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")

        }   

    }
}
Run Code Online (Sandbox Code Playgroud)


Ric*_*III 10

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

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);
Run Code Online (Sandbox Code Playgroud)


ini*_*333 10

这对我有用:

[UIView animateWithDuration:1.0
                animations:^
{
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    self.imageView.transform = CGAffineTransformMakeRotation(0);
}];
Run Code Online (Sandbox Code Playgroud)


Dil*_*lip 9

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

这是我的代码我根据我对速度的需要做了一些小改动:)

的UIImageView + Rotate.h

#import <Foundation/Foundation.h>

@interface UIImageView (Rotate)
- (void)rotate360WithDuration:(CGFloat)duration repeatCount:(float)repeatCount;
- (void)pauseAnimations;
- (void)resumeAnimations;
- (void)stopAllAnimations;
@end
Run Code Online (Sandbox Code Playgroud)

的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
Run Code Online (Sandbox Code Playgroud)


Dav*_*nek 9

这是我作为UIView扩展的快速解决方案.它可以被视为任何UIImageView上的UIActivityIndi​​cator行为的模拟.

import UIKit

extension UIView
{

    /**
    Starts rotating the view around Z axis.

    @param duration Duration of one full 360 degrees rotation. One second is default.
    @param repeatCount How many times the spin should be done. If not provided, the view will spin forever.
    @param clockwise Direction of the rotation. Default is clockwise (true).
     */
    func startZRotation(duration duration: CFTimeInterval = 1, repeatCount: Float = Float.infinity, clockwise: Bool = true)
    {
        if self.layer.animationForKey("transform.rotation.z") != nil {
            return
        }
        let animation = CABasicAnimation(keyPath: "transform.rotation.z")
        let direction = clockwise ? 1.0 : -1.0
        animation.toValue = NSNumber(double: M_PI * 2 * direction)
        animation.duration = duration
        animation.cumulative = true
        animation.repeatCount = repeatCount
        self.layer.addAnimation(animation, forKey:"transform.rotation.z")
    }


    /// Stop rotating the view around Z axis.
    func stopZRotation()
    {
        self.layer.removeAnimationForKey("transform.rotation.z")
    }

}
Run Code Online (Sandbox Code Playgroud)


Vic*_*hoy 9

一个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")
    }
}
Run Code Online (Sandbox Code Playgroud)

请记住调用startRotateviewWillAppear没有viewDidLoad.


yon*_*nja 8

@ram 的回答真的很有帮助。这是答案的 Swift 版本。

斯威夫特 2

private func rotateImageView() {

    UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
        self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(M_PI_2))
        }) { (finished) -> Void in
            if finished {
                self.rotateImageView()
            }
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特 3,4,5

private func rotateImageView() {

    UIView.animate(withDuration: 1, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: { () -> Void in
        self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2)
    }) { (finished) -> Void in
        if finished {
            self.rotateImageView()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 挂个旗子怎么样?所以,也许你有一个用于停止动画的函数:`var stop = false` `private func stopRotation() { stop = true }` 然后,在 `if finished {...}` 中: `if finished { if stop { return} self.rotateImageView() }` (2认同)