Animazione con rotazione a 360 gradi di UIView Infinite?


251

Sto cercando di ruotare di UIImageView360 gradi e ho esaminato diversi tutorial online. Non riuscivo a far funzionare nessuno di loro, senza UIViewfermarsi né saltare in una nuova posizione.

  • Come posso raggiungere questo obiettivo?

L'ultima cosa che ho provato è:

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

Ma se uso 2 * pi, non si muove affatto (poiché è la stessa posizione). Se provo a fare solo pi (180 gradi), funziona, ma se chiamo di nuovo il metodo, ruota indietro.

MODIFICA :

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

non funziona neanche. Va in 180gradi, fa una pausa per una frazione di secondo, quindi si reimposta in 0gradi prima di ricominciare.

Risposte:


334

Ho trovato un metodo (l'ho modificato un po ') che ha funzionato perfettamente per me: la rotazione di 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"];
}

25
#import <QuartzCore / QuartzCore.h>
AlBeebe,

3
aggiungi QuartzCore.framework Project-> Build Phases-> Link Binaries
gjpc

9
Questo è il codice giusto per iOS 3.0 e versioni precedenti ma per i programmatori e i nuovi progetti più recenti, Apple ora avvisa gli utenti di non utilizzare questi metodi nel codice Documenti e @Nate utilizza le animazioni basate su blocchi che Apple preferisce ora
PaulWoodIII

1
@cvsguimaraes Dal documento: "Determina se il valore della proprietà è il valore alla fine del ciclo di ripetizione precedente, più il valore del ciclo di ripetizione corrente."
smad

12
Questo potrebbe aiutare qualcuno, [view.layer removeAllAnimations]; per interrompere l'animazione quando necessario.
Arunit21,

112

Complimenti a Richard J. Ross III per l'idea, ma ho scoperto che il suo codice non era proprio quello di cui avevo bisogno. Il valore predefinito per options, credo, è darti UIViewAnimationOptionCurveEaseInOut, che non sembra giusto in un'animazione continua. Inoltre, ho aggiunto un segno di spunta in modo da poter interrompere l'animazione a un quarto di giro se necessario (non infinito , ma di durata indefinita ), e accelerare l'accelerazione durante i primi 90 gradi e rallentare negli ultimi 90 gradi (dopo che è stata richiesta una fermata):

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

Aggiornare

Ho aggiunto la possibilità di gestire le richieste per ricominciare a girare ( startSpin), mentre la rotazione precedente si sta esaurendo (completando). Esempio di progetto qui su Github .


2
Usando questo, noto una breve pausa nell'animazione ad ogni angolo PI / 2 (90 gradi) e un aumento marginale nell'uso della CPU rispetto alla risposta scelta usando CABasicAnimation. Il metodo CABasicAnimation produce un'animazione perfettamente fluida.
Arjun Mehta,

1
@Dilip, sostituirlo M_PI / 2con - (M_PI / 2). (Scorri il codice a destra per vedere la fine di ogni riga)
Nate

1
@Dilip, non so quale durata stai usando nel tuo codice. Gli stessi 0.5secondi per 90 gradi, come lo sono io? In tal caso, il mio codice non ti consente di fermarti a una frazione di rotazione di 90 gradi. Ti permette di fermarti ad un numero intero di intervalli di 90 gradi, ma aggiunge una rotazione extra di 90 gradi, "facilitata". Quindi, nel codice sopra, se chiami stopSpindopo 0,35 secondi, girerà per altri 0,65 secondi e si fermerà a 180 gradi di rotazione totale. Se hai domande più dettagliate, probabilmente dovresti aprire una nuova domanda. Sentiti libero di collegarti a questa risposta.
Nate,

1
Cosa vedi succedere, @MohitJethwa? Ho un'app che lo utilizza e funziona ancora su iOS 8.1.
Nate,

1
@Hemang, certo, ma non è questa la domanda. Pubblicherei una nuova domanda se questo è ciò che stai cercando di fare.
Nate,

85

In Swift, puoi usare il seguente codice per una rotazione 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)
    }
}

L'arresto è come:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}

Cos'è il kRotationAnimationKey?
Luda,

È una chiave String costante, che ti aiuta a identificare e cercare l'animazione. Può essere qualsiasi stringa che desideri. Nel processo di rimozione, puoi vedere che l'animazione viene cercata e quindi cancellata da questa chiave.
Kádi,

C'è qualche stringa o qualcosa di specifico?
Luda,

Ci scusiamo per la risposta tardiva, ma sì, può essere qualsiasi stringa.
Kádi,

1
// Qualsiasi chiave come nella risposta
Zander Zhang del

67

La risposta di Nate sopra è l'ideale per interrompere e avviare l'animazione e offre un controllo migliore. Sono stato incuriosito dal fatto che il tuo non ha funzionato e il suo lo fa. Volevo condividere qui i miei risultati e una versione più semplice del codice che animasse continuamente un UIView senza bloccarsi.

Questo è il codice che ho usato,

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

Ho usato 'CGAffineTransformRotate' invece di 'CGAffineTransformMakeRotation' perché il primo restituisce il risultato che viene salvato mentre procede l'animazione. Ciò impedirà il salto o il ripristino della vista durante l'animazione.

Un'altra cosa è non usare 'UIViewAnimationOptionRepeat' perché alla fine dell'animazione prima che inizi a ripetersi, ripristina la trasformazione facendo tornare la vista nella sua posizione originale. Invece di ripetere, si ricorre in modo che la trasformazione non venga mai ripristinata al valore originale perché il blocco di animazione praticamente non finisce mai.

E l'ultima cosa è che devi trasformare la vista a passi di 90 gradi (M_PI / 2) invece di 360 o 180 gradi (2 * M_PI o M_PI). Perché la trasformazione avviene come una moltiplicazione matriciale di valori di seno e coseno.

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

Quindi, supponiamo che se si utilizza la trasformazione a 180 gradi, il coseno di 180 produce -1 facendo in modo che la vista si trasformi ogni volta in direzione opposta (la risposta di Note-Nate avrà anche questo problema se si cambia il valore radiante della trasformazione in M_PI). Una trasformazione a 360 gradi sta semplicemente chiedendo alla vista di rimanere dov'era, quindi non si vede alcuna rotazione.


Il punto centrale della mia risposta (e della risposta di Richard da cui deriva) è usare passi di 90 gradi , per evitare di cambiare direzione. 90 gradi funziona anche relativamente bene se non si desidera interrompere la rotazione , poiché molte immagini hanno una simmetria di 180 gradi o 90 gradi.
Nate,

Sì, ho solo pensato di spiegare perché viene utilizzato :)
ram

2
@nik Ma dopo aver impostato il punto di interruzione lì dentro, vedo che non ci sarebbe uno stack overflow perché il blocco di completamento viene chiamato dal sistema, a quel punto rotateImageViewè già finito. Quindi non è davvero ricorsivo in questo senso.
Huggie,

1
Buona risposta +1 per una versione ridotta del codice con la funzionalità desiderata.
Pradeep Mittal,

1
Ho davvero apprezzato la spiegazione del perché non usare UIViewAnimationOptionRepeat.
Jonathan Zhan,

21

Se tutto ciò che vuoi fare è ruotare l'immagine all'infinito, funziona abbastanza bene ed è molto semplice:

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

Nella mia esperienza, questo funziona perfettamente, ma assicurati che la tua immagine sia in grado di ruotare attorno al suo centro senza alcun offset, o l'animazione dell'immagine "salterà" una volta raggiunta la PI.

Per cambiare la direzione della rotazione, cambia il segno di angle( angle *= -1).

Aggiornamento Commenti by @AlexPretzlav mi ha fatto rivedere questo, e mi resi conto che quando ho scritto questo l'immagine che stava ruotando stata rispecchiata lungo sia l'asse verticale e orizzontale, il che significa l'immagine è stata infatti solo ruotando di 90 gradi e poi il ripristino, anche se sembrava come continuava a ruotare tutto intorno.

Quindi, se la tua immagine è come la mia, funzionerà benissimo, tuttavia, se l'immagine non è simmetrica, noterai che lo "snap" torna all'orientamento originale dopo 90 gradi.

Per ruotare un'immagine non simmetrica, stai meglio con la risposta accettata.

Una di queste soluzioni meno eleganti, vista di seguito, ruoterà veramente l'immagine, ma potrebbe esserci una notevole balbuzie al riavvio dell'animazione:

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

Potresti anche farlo solo con i blocchi, come suggerisce @ richard-j-ross-iii, ma otterrai un avviso di mantenimento poiché il blocco si sta catturando da solo:

__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();

1
Questo ha solo ruotato di 1/4 di rotazione per me.
Alex Pretzlav,

@AlexPretzlav Assicurati di aver UIViewAnimationOptionRepeatimpostato l'animazione options.
levigroker,

1
L'ho fatto, ruota di 45 °, quindi passa in loop saltando indietro di 0 ° e ruotando di nuovo di 45 °
Alex Pretzlav

Aggiornato la mia risposta con nuove informazioni sul perché questo "ha funzionato"
levigroker,

21

Il mio contributo con un'estensione Swift dalla soluzione selezionata:

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

Per il conteggio delle ripetizioni, si può usare .infinity.
Rogers,

15

La fantastica risposta di David Rysanek aggiornata 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")

        }   

    }
}

ha funzionato molto bene per me (su un UIButton). Grazie!
agrippa,

mi è piaciuta la tua semplicità
Nicolas Manzini,

10

Usa un quarto di giro e aumenta il turno in modo incrementale.

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

Sono molto nuovo nei blocchi, ma questo metodo genera l'errore "Tipi di puntatore a blocchi incompatibili che inviano 'void (^ const__strong ()' al parametro di tipo 'void (^) (BOOL)'. Ho provato a cambiare il mio metodo di rotazione nel codice nella mia domanda a imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); hai usato e l'animazione ha ancora un leggero ritardo e si ripristina prima del turno successivo
Derek

10

Questo stava funzionando per me:

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

Non sta succedendo per tempi infiniti!
Jeevan il

9

Ho trovato un bel codice in questo repository ,

Ecco il codice da esso ho fatto piccole modifiche in base al mio bisogno di velocità :)

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

@BreadicalMD, la matematica è come programmare, puoi fare la stessa cosa con più modalità. Ciò non significa che solo 1 modo sia corretto e altri no. Sì, ci possono essere dei pro e dei contro in ogni modo, ma ciò non significa che puoi fare domande sul metodo di programmazione di qualcuno, puoi suggerire i contro per questo metodo e i pro per il tuo metodo. Questo commento è davvero offensivo e non dovresti aggiungere commenti come questo.
Dilip

1
Mi dispiace che ti abbia offeso Dilip, ma scrivere 2 * M_PI è oggettivamente più chiaro di ((360 * M_PI) / 180), e scrivere quest'ultimo dimostra una mancanza di comprensione dell'argomento in questione.
BreadicalMD,

1
@BreadicalMD Nessun problema, ma è un buon suggerimento, ho aggiornato il mio codice. Grazie.
Dilip

9

Ecco la mia soluzione rapida come estensione UIView. Potrebbe essere considerato come una simulazione di un comportamento UIActivityIndicator su qualsiasi 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")
    }

}

9

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

E ricordarsi di chiamare startRotatein viewWillAppearnon in viewDidLoad.


3

Puoi anche fare lo stesso tipo di animazione usando UIView e blocchi. Ecco un metodo di estensione di classe che può ruotare la vista da qualsiasi angolo.

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

Questo metodo (chiamata ricorsiva al completamento) funzionerebbe bene con un'animazione in rapido cambiamento? Ad esempio, la durata sarebbe di ca. 1 / 30s in modo da poter modificare la velocità di rotazione dell'oggetto in tempo reale, reagendo ad esempio ai tocchi?
alecail

3

Se qualcuno voleva la soluzione di Nates ma in modo rapido, ecco una traduzione approssimativa veloce:

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

3

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

2

È così che ruoto 360 nella giusta direzione.

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

2

Crea l'animazione

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

Aggiungilo a una vista come questa

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

In che modo questa risposta è diversa? Avrai un codice più pulito se la maggior parte delle tue funzioni restituisce oggetti invece di manipolare alcuni oggetti qua e là.


2

Esistono diversi modi per eseguire l'animazione a 360 gradi con UIView.

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


Ecco una funzione di estensione per UIView che gestisce le operazioni di rotazione di avvio e arresto:

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


Ora usando, chiusura UIView.animation :

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

2

La risposta di @ ram è stata davvero utile. Ecco una versione rapida della risposta.

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

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

come posso interrompere la rotazione?
user2526811

1
Che ne dici di mettere una bandiera? Quindi, forse hai una funzione per interrompere l'animazione: var stop = false private func stopRotation() { stop = true } Quindi, dentro if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja,

Grazie, ho fatto esattamente lo stesso. Grazie per la risposta.
user2526811

1

Ho sviluppato un quadro di animazione brillante che può farti risparmiare il tono del tempo! Usandolo questa animazione può essere creata molto facilmente:

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

per interrompere questa animazione è sufficiente impostare nilsu endlessRotater.

Se sei interessato, dai un'occhiata a: https://github.com/hip4yes/Animatics


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

arbitro


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

1

Swift 5 UIView Extension usando Keyframe Animations

Questo approccio ci consente di utilizzare direttamente 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)

    }

}

2
Mi piacciono i tuoi, anche abbastanza puliti
Nicolas Manzini,

0

Veloce:

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


    }

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

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

-1

Penso che dovresti aggiungere una UIVIewcategoria:

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

UIView di implementazione (Ruota)

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

Non usando questo metodo :)

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

Molto bene. Voglio solo commentare la categoria UIImageView. Grazie lo stesso.
Zgpeace,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.