UIView Animação infinita de rotação de 360 ​​graus?

251

Estou tentando girar UIImageView360 graus e já vi vários tutoriais online. Eu não conseguia fazer nenhum deles funcionar, sem UIViewparar ou pular para uma nova posição.

  • Como posso conseguir isso?

A última coisa que tentei é:

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

Mas se eu usar 2 * pi, ele não se moverá (pois é a mesma posição). Se eu tentar fazer apenas pi (180 graus), funcionará, mas se eu chamar o método novamente, ele girará para trás.

EDIT :

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

também não funciona. Vai para 180graus, faz uma pausa por uma fração de segundo e depois redefine para 0graus antes de começar novamente.

Derek
fonte

Respostas:

334

Encontrei um método (modifiquei-o um pouco) que funcionou perfeitamente para mim: rotação do 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
fonte
25
#import <QuartzCore / QuartzCore.h>
AlBeebe
3
adicionar QuartzCore.framework Projeto-> Desenvolver Phases-> Ligação Binários
gjpc
9
Este é o código certo para iOS 3.0 e abaixo, mas para os programadores mais recentes e novos projetos, a Apple agora avisa os usuários longe destes métodos no código Docs & @Nate usa as animações baseadas em bloco que a Apple agora prefere
PaulWoodIII
1
@cvsguimaraes No documento: "Determina se o valor da propriedade é o valor no final do ciclo de repetição anterior, mais o valor do ciclo de repetição atual".
SMAD
12
Isso pode ajudar alguém, [view.layer removeAllAnimations]; para interromper a animação quando necessário.
Arunit21
112

Parabéns a Richard J. Ross III pela idéia, mas achei que o código dele não era exatamente o que eu precisava. O padrão para options, acredito, é dar a você UIViewAnimationOptionCurveEaseInOut, o que não parece certo em uma animação contínua. Além disso, adicionei uma verificação para que eu pudesse interromper minha animação em um quarto de volta, se necessário (não infinito , mas de duração indefinida ) e fiz a aceleração acelerar durante os primeiros 90 graus e desacelerar nos últimos 90 graus (depois que uma parada foi solicitada):

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

Atualizar

Eu adicionei a capacidade de lidar com solicitações para começar a girar novamente ( startSpin), enquanto a rotação anterior está terminando. Exemplo de projeto aqui no Github .

Nate
fonte
2
Usando isso, percebo uma breve pausa na animação em cada ângulo PI / 2 (90 graus) e um aumento marginal no uso da CPU sobre a resposta escolhida usando CABasicAnimation. O método CABasicAnimation produz uma animação sem falhas.
Arjun Mehta
1
@Dilip, substitua M_PI / 2por - (M_PI / 2). (Role o código para a direita para ver o final de cada linha)
Nate
1
@ Dilip, não sei qual a duração que você está usando no seu código. Os mesmos 0.5segundos por 90 graus, como eu sou? Nesse caso, meu código não permite que você pare em uma fração de uma rotação de 90 graus. Ele permite que você pare em um número inteiro de intervalos de 90 graus, mas adiciona uma rotação extra de 90 graus, "facilitada". Portanto, no código acima, se você ligar stopSpinapós 0,35 segundos, ele girará por mais 0,65 segundos e parará em rotação total de 180 graus. Se você tiver perguntas mais detalhadas, provavelmente deverá abrir uma nova pergunta. Sinta-se livre para vincular a esta resposta.
Nate
1
O que você está vendo acontecer, @MohitJethwa? Eu tenho um aplicativo que usa isso e ainda funciona para mim no iOS 8.1.
Nate
1
@ Hemang, com certeza, mas não é essa a pergunta. Eu postaria uma nova pergunta se é isso que você está tentando fazer.
Nate
85

No Swift, você pode usar o seguinte código para rotação 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 é como:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}
Kádi
fonte
O que é o kRotationAnimationKey?
Luda
É uma chave String constante, que ajuda a identificar e pesquisar a animação. Pode ser qualquer String que você desejar. No processo de remoção, você pode ver que a animação é pesquisada e excluída por essa chave.
Kádi 29/09/15
É alguma string ou algo específico?
Luda
Desculpe pela resposta tardia, mas sim, pode ser qualquer string.
Kádi
1
// Qualquer tecla como na resposta
Zander Zhang
67

A resposta de Nate acima é ideal para parar e iniciar a animação e oferece um melhor controle. Fiquei intrigado porque o seu não funcionou e o dele. Eu queria compartilhar minhas descobertas aqui e uma versão mais simples do código que animaria um UIView continuamente sem parar.

Este é o código que eu usei,

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

Usei 'CGAffineTransformRotate' em vez de 'CGAffineTransformMakeRotation' porque o primeiro retorna o resultado que é salvo conforme a animação prossegue. Isso impedirá o salto ou a redefinição da exibição durante a animação.

Outra coisa é não usar 'UIViewAnimationOptionRepeat' porque, no final da animação, antes de começar a repetir, ela redefine a transformação, fazendo com que a exibição volte à sua posição original. Em vez de repetir, você recursa para que a transformação nunca seja redefinida para o valor original porque o bloco de animação praticamente nunca termina.

E a última coisa é que você deve transformar a exibição em etapas de 90 graus (M_PI / 2) em vez de 360 ​​ou 180 graus (2 * M_PI ou M_PI). Porque a transformação ocorre como uma multiplicação matricial dos valores de seno e cosseno.

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

Então, digamos que se você usar uma transformação de 180 graus, o cosseno de 180 produz -1, transformando a visualização na direção oposta a cada vez (a resposta do Note-Nate também terá esse problema se você alterar o valor radiano da transformação para M_PI). Uma transformação de 360 ​​graus é simplesmente pedir que a vista permaneça onde estava, portanto, você não vê nenhuma rotação.

RAM
fonte
O ponto principal da minha resposta (e da resposta de Richard, da qual deriva) é usar etapas de 90 graus , para evitar mudar de direção. 90 graus também funciona relativamente bem se você quiser interromper a rotação , pois muitas imagens têm simetria de 180 ou 90 graus.
Nate
Sim, eu apenas pensei que eu iria explicar por que ele está sendo usado :)
ram
2
@nik Mas depois de definir o ponto de interrupção, vejo que não haveria estouro de pilha porque o bloco de conclusão é chamado pelo sistema, rotateImageViewjá nessa hora já havia terminado. Portanto, não é realmente recursivo nesse sentido.
huggie
1
Boa resposta +1 para uma versão pequena do código com a funcionalidade desejada.
Pradeep Mittal
1
Eu realmente gostei da explicação de por que não usar UIViewAnimationOptionRepeat.
Jonathan Zhan
21

Se tudo o que você deseja fazer é girar a imagem sem parar, isso funciona muito bem e é muito simples:

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

Na minha experiência, isso funciona perfeitamente, mas certifique-se de que sua imagem seja capaz de girar em torno do centro sem compensações, ou a animação da imagem "saltará" assim que for para o PI.

Para alterar a direção do giro, altere o sinal de angle( angle *= -1).

Atualização Comentários por @AlexPretzlav me fez voltar a esta, e eu percebi que quando eu escrevi essa a imagem que eu estava girando foi espelhado ao longo tanto o eixo vertical e horizontal, ou seja, a imagem foi de fato única rotação de 90 graus e, em seguida, reiniciar, embora parecia como continuava girando a toda a volta.

Portanto, se sua imagem é como a minha, isso funcionará muito bem; no entanto, se a imagem não for simétrica, você notará o "snap" de volta à orientação original depois de 90 graus.

Para girar uma imagem não simétrica, é melhor ter a resposta aceita.

Uma dessas soluções menos elegantes, vista abaixo, realmente girará a imagem, mas pode haver uma gagueira perceptível quando a animação for reiniciada:

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

Você também pode fazer isso apenas com blocos, como sugere @ richard-j-ross-iii, mas você receberá um aviso de loop de retenção, pois o bloco está se capturando:

__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
fonte
1
Isso girou apenas 1/4 de uma rotação para mim.
Alex Pretzlav
@AlexPretzlav Certifique-se de ter UIViewAnimationOptionRepeatdefinido sua animação options.
precisa saber é o seguinte
1
Fiz, que gira 45 °, e, em seguida, circula por saltar de volta para 0 ° e 45 ° de rotação novamente
Alex Pretzlav
Atualizei a minha resposta com novas informações sobre por que isso "trabalhou"
levigroker
21

Minha contribuição com uma extensão rápida da solução verificada:

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

Descontinuada :

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
fonte
Para a contagem de repetição, pode-se usar .infinity.
Rogers,
15

A resposta impressionante de David Rysanek atualizada para 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
fonte
funcionou muito bem para mim (em um UIButton). Obrigado!
Agrippa
Gostei da sua simplicidade
Nicolas Manzini
10

Use um quarto de volta e aumente incrementalmente.

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
fonte
Eu sou muito novo em blocos, mas esse método gera o erro "Tipos de ponteiros de bloco incompatíveis enviando 'void (^ const__strong ()' para o parâmetro do tipo 'void (^) (BOOL)'. Tentei alterar meu método de rotação no código na minha pergunta ao imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); que você usou, e a animação ainda tem um ligeiro atraso e redefine antes do próximo turno.
Derek
10

Isso estava funcionando para mim:

[UIView animateWithDuration:1.0
                animations:^
{
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    self.imageView.transform = CGAffineTransformMakeRotation(0);
}];
inigo333
fonte
Isso não está acontecendo por tempos infinitos!
precisa saber é o seguinte
9

Eu encontrei um bom código neste repositório ,

Aqui está o código que eu fiz pequenas alterações de acordo com a minha necessidade de velocidade :)

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
fonte
@BreadicalMD, Math é como programação, você pode fazer a mesma coisa de várias maneiras. Isso não significa que apenas uma maneira esteja correta e outras não. Sim, pode haver alguns prós e contras em todos os aspectos, mas isso não significa que você possa questionar sobre o método de programação de alguém. Você pode sugerir contras para esse método e prós para o seu método. Este comentário é realmente ofensivo e você não deve adicionar comentários como este.
Dilip 15/05
1
Lamento ofender você Dilip, mas escrever 2 * M_PI é objetivamente mais claro que ((360 * M_PI) / 180), e escrever o último demonstra uma falta de compreensão do assunto em questão.
BreadicalMD
1
@BreadicalMD Não se preocupe, mas é uma boa sugestão, eu atualizei meu código. Thanx.
Dilip
9

Aqui está minha solução rápida como uma extensão UIView. Pode ser considerado como uma simulação de um comportamento UIActivityIndicator em qualquer 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
fonte
9

Uma versão 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 lembre-se de ligar startRotatepara viewWillAppearnão entrar viewDidLoad.

Victor Choy
fonte
3

Você também pode fazer o mesmo tipo de animação usando UIView e blocos. Aqui está um método de extensão de classe que pode girar a vista por qualquer â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
fonte
Esse método (chamada recursiva na conclusão) funcionaria bem com uma animação em rápida mudança? Por exemplo, a duração seria de aprox. 1/30s para que eu possa modificar a velocidade de rotação do objeto em tempo real, reagindo a toques, por exemplo?
alecail
3

Se alguém quisesse a solução dos nates, mas rapidamente, aqui está uma tradução rápida:

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
fonte
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
fonte
2

É assim que eu giro 360 na direção certa.

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

Crie a animação

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

Adicione-o a uma visualização como esta

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

Qual é a diferença desta resposta? Você terá um código de maneira mais limpa se a maioria de suas funções retornar objetos em vez de apenas manipular alguns objetos aqui e ali.

hfossli
fonte
2

Existem várias maneiras de executar animação em 360 graus com o 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")


Aqui estão as funções de extensão do UIView que manipulam as operações de início e parada da rotação:

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


Agora, usando o fechamento UIView.animation :

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

A resposta do @ ram foi realmente útil. Aqui está uma versão rápida da resposta.

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
fonte
como posso parar a rotação?
user2526811
1
Que tal você colocar uma bandeira? Então, talvez você tenha uma função para interromper a animação: var stop = false private func stopRotation() { stop = true } Então, dentro de if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja
Obrigado, eu fiz exatamente o mesmo. Obrigado pela sua resposta.
user2526811
1

Eu desenvolvi uma estrutura de animação brilhante que pode economizar seu tom de tempo! Usando-o, esta animação pode ser criada com muita facilidade:

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 parar esta animação, basta definir nilcomo endlessRotater.

Se você estiver interessado, dê uma olhada: https://github.com/hip4yes/Animatics

Nikita Arkhipov
fonte
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()
        }
    }

Ref.

Mohammad Zaid Pathan
fonte
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
fonte
1

Extensão UIView Swift 5 usando animações de quadro-chave

Essa abordagem nos permite usar diretamente o 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
fonte
2
Eu gosto do seu, também muito limpo
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
fonte
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
fonte
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
fonte
-1

Acho melhor você adicionar uma UIVIewcategoria:

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

Implementação UIView (Rodar)

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

Não usando este método :)

  • (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
fonte
Bem feito. Eu só quero comentar sobre a categoria UIImageView. Obrigado a todos.
Zgpeace