UIView Infinite animación de rotación de 360 ​​grados?

251

Estoy tratando de rotar UIImageView360 grados y he visto varios tutoriales en línea. No pude conseguir que ninguno de ellos funcionara, sin UIViewparar ni saltar a una nueva posición.

  • ¿Cómo puedo conseguir esto?

Lo último que he probado es:

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

Pero si uso 2 * pi, no se mueve en absoluto (ya que es la misma posición). Si intento hacer solo pi (180 grados), funciona, pero si vuelvo a llamar al método, gira hacia atrás.

EDITAR :

[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!");
                 }];

tampoco funciona Va a 180grados, hace una pausa por una fracción de segundo, luego se restablece a 0grados antes de que comience nuevamente.

Derek
fuente

Respuestas:

334

Encontré un método (lo modifiqué un poco) que funcionó perfectamente para mí: rotación de 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"];
}
Derek
fuente
25
#import <QuartzCore / QuartzCore.h>
AlBeebe
3
agregue QuartzCore.framework Project-> Build Phases-> Link Binaries
gjpc
99
Este es el código correcto para iOS 3.0 y versiones inferiores, pero para los programadores más nuevos y los proyectos nuevos, Apple ahora advierte a los usuarios que no usen estos métodos en el código Docs & @Nate que usa las animaciones basadas en bloques que Apple ahora prefiere
PaulWoodIII
1
@cvsguimaraes Del documento: "Determina si el valor de la propiedad es el valor al final del ciclo de repetición anterior, más el valor del ciclo de repetición actual".
smad
12
Esto podría ayudar a alguien, [view.layer removeAllAnimations]; para detener la animación cuando sea necesario.
arunit21
112

Felicitaciones a Richard J. Ross III por la idea, pero descubrí que su código no era exactamente lo que necesitaba. El valor predeterminado paraoptions , creo, es darle UIViewAnimationOptionCurveEaseInOut, que no se ve bien en una animación continua. Además, agregué un cheque para poder detener mi animación en un cuarto de vuelta si fuera necesario (no infinito , sino de duración indefinida ), e hice que la aceleración aumentara durante los primeros 90 grados, y desacelere durante los últimos 90 grados (después de solicitar una parada):

// 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;
}

Actualizar

Agregué la capacidad de manejar solicitudes para comenzar a girar nuevamente (startSpin ), mientras que el giro anterior se está terminando (completando). Proyecto de muestra aquí en Github .

Nate
fuente
2
Usando esto, noto una breve pausa en la animación en cada ángulo PI / 2 (90 grados) y un aumento marginal en el uso de la CPU sobre la respuesta elegida usando CABasicAnimation. El método CABasicAnimation produce una animación perfectamente fluida.
Arjun Mehta
1
@Dilip, reemplace M_PI / 2con - (M_PI / 2). (Desplácese hacia la derecha para ver el final de cada línea)
Nate
1
@Dilip, no sé qué duración estás usando en tu código. ¿Los mismos 0.5segundos por cada 90 grados, como yo? Si es así, mi código no le permite detenerse en una fracción de una rotación de 90 grados. Le permite detenerse en un número entero de intervalos de 90 grados, pero agrega una rotación adicional de 90 grados, "facilitada". Entonces, en el código anterior, si llama stopSpindespués de 0.35 segundos, girará durante otros 0.65 segundos y se detendrá a una rotación total de 180 grados. Si tiene preguntas más detalladas, probablemente debería abrir una nueva pregunta. Siéntase libre de vincular a esta respuesta.
Nate
1
¿Qué ves que pasa, @MohitJethwa? Tengo una aplicación que usa esto, y todavía funciona para mí en iOS 8.1.
Nate
1
@Hemang, claro, pero esa no era la pregunta. Publicaría una nueva pregunta si esto es lo que estás tratando de hacer.
Nate
85

En Swift, puede usar el siguiente código para rotación infinita:

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

Swift 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)
    }
}

Parar es como:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}
Kádi
fuente
¿Qué es la kRotationAnimationKey?
Luda
Es una clave de cadena constante, que le ayuda a identificar y buscar la animación. Puede ser cualquier Cadena que desee. En el proceso de eliminación, puede ver que la animación busca y luego elimina esta animación.
Kádi
¿Es alguna cadena o algo específico?
Luda
Perdón por la respuesta tardía, pero sí, puede ser cualquier cadena.
Kádi
1
// Cualquier clave como en la respuesta
Zander Zhang
67

La respuesta anterior de Nate es ideal para detener e iniciar la animación y ofrece un mejor control. Me intrigaba por qué el suyo no funcionaba y el suyo sí. Quería compartir mis hallazgos aquí y una versión más simple del código que animaría una UIView continuamente sin detenerse.

Este es el código que usé,

- (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];
        }
    }];
}

Usé 'CGAffineTransformRotate' en lugar de 'CGAffineTransformMakeRotation' porque el primero devuelve el resultado que se guarda a medida que avanza la animación. Esto evitará el salto o el restablecimiento de la vista durante la animación.

Otra cosa es no usar 'UIViewAnimationOptionRepeat' porque al final de la animación antes de que comience a repetirse, restablece la transformación haciendo que la vista vuelva a su posición original. En lugar de una repetición, usted recurre para que la transformación nunca se restablezca al valor original porque el bloque de animación prácticamente nunca termina.

Y lo último es que debe transformar la vista en pasos de 90 grados (M_PI / 2) en lugar de 360 ​​o 180 grados (2 * M_PI o M_PI). Porque la transformación se produce como una multiplicación matricial de los valores seno y coseno.

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

Entonces, digamos que si usa una transformación de 180 grados, el coseno de 180 produce -1 haciendo que la vista se transforme en dirección opuesta cada vez (la respuesta de Note-Nate también tendrá este problema si cambia el valor en radianes de la transformación a M_PI). Una transformación de 360 ​​grados es simplemente pedirle a la vista que permanezca donde estaba, por lo tanto, no ve ninguna rotación en absoluto.

RAM
fuente
El punto principal de mi respuesta (y la respuesta de Richard de la que deriva) es usar pasos de 90 grados , para evitar cambiar de dirección. 90 grados también funciona relativamente bien si alguna vez desea detener la rotación , ya que muchas imágenes tienen una simetría de 180 grados o de 90 grados.
Nate
Sí, solo pensé en explicar por qué se está usando :)
ram
2
@nik Pero después de establecer el punto de interrupción allí, veo que no habrá desbordamiento de pila porque el sistema llama al bloque de finalización, para ese momento rotateImageViewya ha terminado. Entonces no es realmente recursivo en ese sentido.
huggie
1
Buena respuesta +1 para una versión pequeña de código con la funcionalidad deseada.
Pradeep Mittal
1
Realmente aprecié la explicación de por qué no usar UIViewAnimationOptionRepeat.
Jonathan Zhan
21

Si todo lo que quiere hacer es rotar la imagen sin cesar, esto funciona bastante bien y es muy simple:

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];

En mi experiencia, esto funciona a la perfección, pero asegúrese de que su imagen sea capaz de girar alrededor de su centro sin ningún desplazamiento, o la animación de la imagen "saltará" una vez que llegue a PI.

Para cambiar la dirección del giro, cambie el signo de angle(angle *= -1 ).

Los comentarios de actualización de @AlexPretzlav me hicieron volver a visitar esto, y me di cuenta de que cuando escribí esto, la imagen que estaba girando se reflejó a lo largo del eje vertical y horizontal, lo que significa que la imagen en realidad solo giraba 90 grados y luego se reiniciaba, aunque parecía como continuaba girando todo el camino.

Entonces, si su imagen es como la mía, esto funcionará muy bien, sin embargo, si la imagen no es simétrica, notará el "ajuste" de nuevo a la orientación original después de 90 grados.

Para rotar una imagen no simétrica, es mejor que con la respuesta aceptada.

Una de estas soluciones menos elegantes, que se ve a continuación, realmente rotará la imagen, pero puede haber un tartamudeo notable cuando se reinicia la animación:

- (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];
    }];
}

También puede hacer esto solo con bloques, como sugiere @ richard-j-ross-iii, pero recibirá una advertencia de bucle de retención ya que el bloque se está capturando a sí mismo:

__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();
levigroker
fuente
1
Esto solo giró 1/4 de vuelta para mí.
Alex Pretzlav
@AlexPretzlav Asegúrese de haber UIViewAnimationOptionRepeatconfigurado su animación options.
levigroker
1
Lo hice, gira 45 °, y luego gira saltando de nuevo a 0 ° y girando 45 ° nuevamente
Alex Pretzlav
Actualicé mi respuesta con nueva información sobre por qué esto "funcionó"
levigroker
21

Mi contribución con una extensión Swift de la solución marcada:

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

Obsoleto :

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")
    }
}
Kevin ABRIOUX
fuente
Para el recuento de repetición, uno puede usar .infinity.
Mr Rogers
15

La increíble respuesta de David Rysanek actualizada a 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")

        }   

    }
}
Geoff H
fuente
funcionó muy bien para mí (en un UIButton). ¡Gracias!
agrippa
me gustó tu simplicidad
Nicolas Manzini
10

Use cuarto de turno y aumente el turno gradualmente.

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);
Richard J. Ross III
fuente
Soy muy nuevo en los bloques, pero este método arroja el error "Tipos de puntero de bloque incompatibles que envían 'void (^ const__strong ()' al parámetro de tipo 'void (^) (BOOL)'. Intenté cambiar mi método de rotación en el código en mi pregunta a imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); usaste, y la animación todavía tiene un ligero retraso, y se restablece antes del próximo turno.
Derek
10

Esto estaba funcionando para mí:

[UIView animateWithDuration:1.0
                animations:^
{
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    self.imageView.transform = CGAffineTransformMakeRotation(0);
}];
inigo333
fuente
¡No está sucediendo por tiempos infinitos!
porJeevan
9

He encontrado un buen código en este repositorio ,

Aquí está el código, he realizado pequeños cambios según mi necesidad de velocidad :)

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
Dilip
fuente
@BreadicalMD, Math es como programar, puedes hacer lo mismo de varias maneras. Eso no significa que solo 1 forma sea correcta y otras no. Sí, puede haber algunos pros y contras para todos los sentidos, pero eso no significa que pueda preguntar sobre el método de programación de alguien, puede sugerir contras para este método y pros para su método. Este comentario es realmente ofensivo, y no debe agregar comentarios como este.
Dilip
1
Lamento haberte ofendido Dilip, pero escribir 2 * M_PI es objetivamente más claro que ((360 * M_PI) / 180), y escribir esto demuestra una falta de comprensión del tema en cuestión.
BreadicalMD
1
@BreadicalMD No se preocupe, pero es una buena sugerencia, he actualizado mi código. Gracias
Dilip
9

Aquí está mi solución rápida como una extensión UIView. Podría considerarse como una simulación de un comportamiento UIActivityIndicator en cualquier UIImageView.

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

}
David Rysanek
fuente
9

Una versión 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")
    }
}

Y recuerde llamar startRotateen viewWillAppearno en viewDidLoad.

Victor Choy
fuente
3

También puedes hacer el mismo tipo de animación usando UIView y bloques. Aquí hay un método de extensión de clase que puede rotar la vista en cualquier ángulo.

- (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();
    }
}
MiKL
fuente
¿Funcionaría bien este método (llamada recursiva al finalizar) con una animación que cambia rápidamente? Por ejemplo, la duración sería de aprox. ¿1 / 30s para que pueda modificar la velocidad de rotación del objeto en tiempo real, reaccionando a los toques, por ejemplo?
alecail
3

Si alguien quería la solución de Nates pero de manera rápida, aquí hay una traducción rápida y aproximada:

class SomeClass: UIViewController {

    var animating : Bool = false
    @IBOutlet weak var activityIndicatorImage: UIImageView!

    func startSpinning() {
        if(!animating) {
            animating = true;
            spinWithOptions(UIViewAnimationOptions.CurveEaseIn);
        }
    }

    func stopSpinning() {
        animating = false
    }

    func spinWithOptions(options: UIViewAnimationOptions) {
        UIView.animateWithDuration(0.5, delay: 0.0, options: options, animations: { () -> Void in
            let val : CGFloat = CGFloat((M_PI / Double(2.0)));
            self.activityIndicatorImage.transform = CGAffineTransformRotate(self.activityIndicatorImage.transform,val)
        }) { (finished: Bool) -> Void in

            if(finished) {
                if(self.animating){
                    self.spinWithOptions(UIViewAnimationOptions.CurveLinear)
                } else if (options != UIViewAnimationOptions.CurveEaseOut) {
                    self.spinWithOptions(UIViewAnimationOptions.CurveEaseOut)
                }
            }

        }
    }

    override func viewDidLoad() {
        startSpinning()
    }
}
Adrian_H
fuente
3

para xamarin ios:

public static void RotateAnimation (this UIView view, float duration=1, float rotations=1, float repeat=int.MaxValue)
{
    var rotationAnimation = CABasicAnimation.FromKeyPath ("transform.rotation.z");
    rotationAnimation.To = new NSNumber (Math.PI * 2.0 /* full rotation*/ * 1 * 1);
    rotationAnimation.Duration = 1;
    rotationAnimation.Cumulative = true;
    rotationAnimation.RepeatCount = int.MaxValue;
    rotationAnimation.RemovedOnCompletion = false;
    view.Layer.AddAnimation (rotationAnimation, "rotationAnimation");
}
Danil Shaykhutdinov
fuente
2

Así es como giro 360 en la dirección correcta.

[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveLinear
                     animations:^{
                         [imageIndView setTransform:CGAffineTransformRotate([imageIndView transform], M_PI-0.00001f)];
                     } completion:nil];
Pavel
fuente
2

Crea la animación

- (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;
}

Agréguelo a una vista como esta

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

¿Cómo es diferente esta respuesta? Tendrá un código más limpio si la mayoría de sus funciones devuelve objetos en lugar de simplemente manipular algunos objetos aquí y allá.

hfossli
fuente
2

Existen diferentes formas de realizar animaciones de 360 ​​grados con UIView.

Usando CABasicAnimation

var rotationAnimation = CABasicAnimation()
rotationAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
rotationAnimation.toValue = NSNumber(value: (Double.pi))
rotationAnimation.duration = 1.0
rotationAnimation.isCumulative = true
rotationAnimation.repeatCount = 100.0
view.layer.add(rotationAnimation, forKey: "rotationAnimation")


Aquí hay una extensión de funciones para UIView que maneja las operaciones de rotación de inicio y parada:

extension UIView {

    // Start rotation
    func startRotation() {
        let rotation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotation.fromValue = 0
        rotation.toValue = NSNumber(value: Double.pi)
        rotation.duration = 1.0
        rotation.isCumulative = true
        rotation.repeatCount = FLT_MAX
        self.layer.add(rotation, forKey: "rotationAnimation")
    }

    // Stop rotation
    func stopRotation() {
        self.layer.removeAnimation(forKey: "rotationAnimation")
    }
}


Ahora usando, cierre UIView.animation :

UIView.animate(withDuration: 0.5, animations: { 
      view.transform = CGAffineTransform(rotationAngle: (CGFloat(Double.pi)) 
}) { (isAnimationComplete) in
    // Animation completed 
}
Krunal
fuente
2

La respuesta de @ ram fue realmente útil. Aquí hay una versión rápida de la respuesta.

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()
            }
    }
}

Swift 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()
        }
    }
}
yoninja
fuente
¿Cómo puedo detener la rotación?
user2526811
1
¿Qué tal si pones una bandera? Entonces, quizás tengas una función para detener la animación: var stop = false private func stopRotation() { stop = true } Luego, adentro if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja
Gracias, hice exactamente lo mismo. Gracias por su respuesta.
user2526811
1

¡He desarrollado un marco de animación brillante que puede ahorrarle mucho tiempo! Al usarlo, esta animación se puede crear muy fácilmente:

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()
}

para detener esta animación simplemente configúrelo nilen endlessRotater.

Si está interesado, eche un vistazo: https://github.com/hip4yes/Animatics

Nikita Arkhipov
fuente
1

Swift 4 ,

func rotateImage(image: UIImageView) {
        UIView.animate(withDuration: 1, animations: {
            image.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
            image.transform = CGAffineTransform.identity
        }) { (completed) in
            self.rotateImage()
        }
    }

Árbitro

Mohammad Zaid Pathan
fuente
1

Swift 4.0

func rotateImageView()
{
    UIView.animate(withDuration: 0.3, delay: 0, options: .curveLinear, animations: {() -> Void in
        self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2)
    }, completion: {(_ finished: Bool) -> Void in
        if finished {
            rotateImageView()
        }
    })
}
Abhishek Jain
fuente
1

Extensión Swift 5 UIView usando animaciones de fotogramas clave

Este enfoque nos permite usar directamente UIView.AnimationOptions.repeat

public extension UIView {

    func animateRotation(duration: TimeInterval, repeat: Bool, completion: ((Bool) -> ())?) {

        var options = UIView.KeyframeAnimationOptions(rawValue: UIView.AnimationOptions.curveLinear.rawValue)

        if `repeat` {
            options.insert(.repeat)
        }

        UIView.animateKeyframes(withDuration: duration, delay: 0, options: options, animations: {

            UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.25, animations: {
                self.transform = CGAffineTransform(rotationAngle: CGFloat.pi/2)
            })

            UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.25, animations: {
                self.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
            })

            UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.25, animations: {
                self.transform = CGAffineTransform(rotationAngle: 3*CGFloat.pi/2)
            })

            UIView.addKeyframe(withRelativeStartTime: 0.75, relativeDuration: 0.25, animations: {
                self.transform = CGAffineTransform(rotationAngle: 2*CGFloat.pi)
            })

        }, completion: completion)

    }

}
Brody Robertson
fuente
2
Me gusta el tuyo, muy limpio también
Nicolas Manzini
0

Rápido:

func runSpinAnimationOnView(view:UIView , duration:Float, rotations:Double, repeatt:Float ) ->()
    {
        let rotationAnimation=CABasicAnimation();

        rotationAnimation.keyPath="transform.rotation.z"

        let toValue = M_PI * 2.0 * rotations ;


        // passing it a float
        let someInterval = CFTimeInterval(duration)

        rotationAnimation.toValue=toValue;
        rotationAnimation.duration=someInterval;
        rotationAnimation.cumulative=true;
        rotationAnimation.repeatCount=repeatt;
        view.layer.addAnimation(rotationAnimation, forKey: "rotationAnimation")


    }
Erhan Demirci
fuente
0

Swift 3:

 var rotationAnimation = CABasicAnimation()
     rotationAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
     rotationAnimation.toValue = NSNumber(value: (M_PI * 2.0))
     rotationAnimation.duration = 2.0
     rotationAnimation.isCumulative = true
     rotationAnimation.repeatCount = 10.0
     view.layer.add(rotationAnimation, forKey: "rotationAnimation")
Deepak Carpenter
fuente
0
let val = CGFloat(M_PI_2)

UIView.animate(withDuration: 1, delay: 0, options: [.repeat, .curveLinear], animations: {
        self.viewToRotate.transform = self.viewToRotate.transform.rotated(by: val)
})
Warif Akhand Rishi
fuente
-1

Creo que deberías agregar una UIVIewCategoría:

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

Implementación UIView (Rotar)

  • (void)remrotate360WithDuration:(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"];
    }

No estoy usando estos métodos :)

  • (void)remstopAllAnimations
    {
        [self.layer removeAllAnimations];
    };
  • (void)rempauseAnimations
    {
        [self rempauseLayer:self.layer];
    }
  • (void)remresumeAnimations
    {
        [self remresumeLayer:self.layer];
    }
  • (void)rempauseLayer:(CALayer *)layer
    {
        CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
        layer.speed = 0.0;
        layer.timeOffset = pausedTime;
    }
  • (void)remresumeLayer:(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;
    }
Zgpeace
fuente
Bien hecho. Solo quiero comentar sobre la categoría UIImageView. Gracias de todos modos.
Zgpeace