UIView Бесконечная анимация вращения на 360 градусов?

251

Я пытаюсь повернуть UIImageView 360 градусов и посмотрел несколько онлайн-уроков. Я не мог заставить никого из них работать без UIViewостановки или прыжка на новую позицию.

  • Как мне этого добиться?

Последнее, что я попробовал, это:

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

Но если я использую 2 * пи, он вообще не двигается (поскольку это одна и та же позиция). Если я пытаюсь сделать только пи (180 градусов), это работает, но если я снова вызываю метод, он поворачивается назад.

РЕДАКТИРОВАТЬ :

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

тоже не работает. Он идет в 180градусах, делает паузу на долю секунды, затем сбрасывается обратно в 0градусы, прежде чем снова начнется.

Дерек
источник

Ответы:

334

Нашел метод (я его немного изменил), который отлично сработал для меня: вращение 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
добавить QuartzCore.framework Project-> Фазы сборки-> Двоичные файлы ссылок
gjpc
9
Это правильный код для iOS 3.0 и ниже, но для новых программистов и новых проектов Apple теперь предупреждает пользователей об этих методах в коде Docs & @Nate, используя анимацию на основе блоков, которую сейчас предпочитает Apple
PaulWoodIII
1
@cvsguimaraes Из документа: «Определяет, является ли значение свойства значением в конце предыдущего цикла повторения плюс значение текущего цикла повторения».
Смэд
13
Это может помочь кому-то, [view.layer removeAllAnimations]; остановить анимацию, когда это необходимо.
arunit21
112

Спасибо Ричарду Дж. Россу III за идею, но я обнаружил, что его код не совсем то, что мне нужно. optionsЯ полагаю, что по умолчанию вам дают UIViewAnimationOptionCurveEaseInOut, что не выглядит правильно в непрерывной анимации. Кроме того, я добавил проверку, чтобы при необходимости остановить анимацию на четном четверти оборота (не бесконечно , а на неопределенное время), и ускорил ускорение в течение первых 90 градусов и замедлил в течение последних 90 градусов. (после запроса на остановку):

// an ivar for your class:
BOOL animating;

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

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

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

Обновить

Я добавил возможность обрабатывать запросы, чтобы снова начать вращаться ( startSpin), пока предыдущий вращение завершается (завершается). Пример проекта здесь, на Github .

Nate
источник
2
Используя это, я замечаю короткую паузу в анимации под каждым углом PI / 2 (90 градусов) и незначительное увеличение использования ЦП по сравнению с выбранным ответом с использованием CABasicAnimation. Метод CABasicAnimation создает безупречно плавную анимацию.
Арджун Мехта
1
@Dilip, заменить M_PI / 2на - (M_PI / 2). (Прокрутите код вправо, чтобы увидеть конец каждой строки)
Нейт
1
@Dilip, я не знаю, какую продолжительность вы используете в своем коде. Те же 0.5секунды на 90 градусов, как и я? Если да, то мой код не позволяет останавливаться на фракции вращения в 90 градусов. Он позволяет вам останавливаться с интервалом в 90 градусов, но добавляет один дополнительный поворот на 90 градусов, «ослабленный». Таким образом, в приведенном выше коде, если вы позвоните stopSpinчерез 0,35 секунды, он будет вращаться еще 0,65 секунды и остановится при общем повороте на 180 градусов. Если у вас есть более подробные вопросы, вам, вероятно, следует открыть новый вопрос. Не стесняйтесь ссылаться на этот ответ.
Nate
1
Что ты видишь, @MohitJethwa? У меня есть приложение, которое использует это, и оно все еще работает для меня на iOS 8.1.
конец
1
@ Hemang, конечно, но это не тот вопрос. Я бы отправил новый вопрос, если это то, что вы пытаетесь сделать.
Nate
85

В Swift вы можете использовать следующий код для бесконечного вращения:

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

Свифт 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)
    }
}

Остановка это как:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}
Кади
источник
Что такое kRotationAnimationKey?
Люда
Это постоянный строковый ключ, который помогает вам идентифицировать и искать анимацию. Это может быть любая строка, которую вы хотите. В процессе удаления вы видите, что анимация ищется и затем удаляется этим ключом.
Кади
Это какая-то строка или что-то конкретное?
Люда
Извините за поздний ответ, но да, это может быть любая строка.
Кади,
1
// Любая клавиша, как в ответе
Зандер Чжан,
67

Ответ Нейта выше идеален для остановки и запуска анимации и дает лучший контроль. Я был заинтригован, почему ваш не работает, а он работает. Я хотел бы поделиться здесь своими выводами и более простой версией кода, который бы непрерывно анимировал UIView без сбоев.

Это код, который я использовал,

- (void)rotateImageView
{
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        [self.imageView setTransform:CGAffineTransformRotate(self.imageView.transform, M_PI_2)];
    }completion:^(BOOL finished){
        if (finished) {
            [self rotateImageView];
        }
    }];
}

Я использовал «CGAffineTransformRotate» вместо «CGAffineTransformMakeRotation», потому что первый возвращает результат, который сохраняется в процессе анимации. Это предотвратит скачок или сброс вида во время анимации.

Другое дело не использовать UIViewAnimationOptionRepeat, потому что в конце анимации, прежде чем он начинает повторяться, он сбрасывает преобразование, заставляя представление вернуться в исходное положение. Вместо повтора вы выполняете рекурсию, чтобы преобразование никогда не сбрасывалось до исходного значения, поскольку блок анимации практически никогда не заканчивается.

И последнее, вы должны преобразовать вид с шагом 90 градусов (M_PI / 2) вместо 360 или 180 градусов (2 * M_PI или M_PI). Потому что преобразование происходит как матричное умножение значений синуса и косинуса.

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

Так, скажем, если вы используете 180-градусное преобразование, косинус 180 дает -1, заставляя представление трансформироваться в противоположном направлении каждый раз (ответ Нота-Нейта также будет иметь эту проблему, если вы измените радианое значение преобразования на M_PI). 360-градусное преобразование просто просит вид остаться там, где он был, следовательно, вы вообще не видите никакого поворота.

ОЗУ
источник
Весь смысл моего ответа (и ответа Ричарда, из которого он вытекает) состоит в том, чтобы использовать 90-градусные шаги, чтобы избежать переключения направлений. 90 градусов также работает относительно хорошо, если вы когда-нибудь захотите остановить вращение , так как многие изображения имеют симметрию 180 или 90 градусов.
Нейт
Да, я просто подумал, что объясню, почему он используется :)
ram
2
@nik Но после установки точки останова я вижу, что не будет переполнения стека, потому что блок завершения вызывается системой, к тому времени rotateImageViewуже завершился. Так что это не совсем рекурсивно в этом смысле.
Хагги
1
Хороший ответ +1 для небольшой версии кода с желаемым функционалом.
Прадип Миттал
1
Я действительно оценил объяснение того, почему не следует использовать UIViewAnimationOptionRepeat.
Джонатан Жан
21

Если все, что вы хотите сделать, это вращать изображение бесконечно, это работает довольно хорошо и очень просто:

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

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

По моему опыту, это работает безупречно, но будьте уверены, что ваше изображение способно вращаться вокруг его центра без каких-либо смещений, иначе анимация изображения будет «прыгать», как только она перейдет к PI.

Чтобы изменить направление вращения, измените знак angle( angle *= -1).

Обновление Комментариев @AlexPretzlav заставило меня вернуться к этому, и я понял, что когда я писал это, изображение, которое я вращал, отражалось вдоль вертикальной и горизонтальной оси, то есть изображение действительно вращалось только на 90 градусов и затем сбрасывалось, хотя выглядело как это продолжало вращаться полностью вокруг.

Таким образом, если ваше изображение похоже на мое, это будет прекрасно работать, однако, если изображение не симметрично, вы заметите «защелкивание» к исходной ориентации после 90 градусов.

Чтобы повернуть несимметричное изображение, вам лучше принять принятый ответ.

Одно из этих менее изящных решений, показанных ниже, будет действительно вращать изображение, но при перезапуске анимации может возникнуть заметное заикание:

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

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

Вы также можете сделать это только с блоками, как предлагает @ richard-j-ross-iii, но вы получите предупреждение о сохранении цикла, так как блок захватывает сам себя:

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

    [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
        self.imageView.transform = rotateTransform;
    } completion:^(BOOL finished) {
        spin();
    }];
};
spin();
levigroker
источник
1
Это вращалось только на 1/4 оборота для меня.
Алексей Прецлав,
@AlexPretzlav Убедитесь, что вы UIViewAnimationOptionRepeatустановили анимацию options.
левигрокер
1
Я сделал это, он вращается на 45 °, а затем зацикливается, перепрыгивая назад на 0 ° и снова вращаясь на 45 °
Алекс Претцлав
Обновил мой ответ новой информацией о том, почему это «сработало»
левигрокер
21

Мой вклад с расширением Swift от проверенного решения:

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

Устаревший:

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")
    }
}
Кевин ABRIOUX
источник
Для подсчета повторов можно использовать .infinity.
Мистер Роджерс
15

Удивительный ответ Дэвида Рисанека обновлен до 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")

        }   

    }
}
Джефф Х
источник
работал очень хорошо для меня (на UIButton). Спасибо!
Агриппа
Николас Манзини
10

Используйте четверть оборота и увеличивайте его постепенно.

void (^block)() = ^{
    imageToMove.transform = CGAffineTransformRotate(imageToMove.transform, M_PI / 2);
}

void (^completion)(BOOL) = ^(BOOL finished){
    [UIView animateWithDuration:1.0
                          delay:0.0
                        options:0
                     animations:block
                     completion:completion];
}

completion(YES);
Ричард Дж. Росс III
источник
Я очень плохо знаком с блоками, но этот метод выдает ошибку «Несовместимые типы указателей блоков, посылающие« void (^ const__strong () »параметру типа« void (^) (BOOL) ». Я попытался изменить метод вращения в коде в моем вопросе к imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); вы использовали, и анимация все еще имеет небольшую задержку и сбрасывается до следующего поворота
Дерек
10

Это работает для меня:

[UIView animateWithDuration:1.0
                animations:^
{
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    self.imageView.transform = CGAffineTransformMakeRotation(0);
}];
inigo333
источник
Это не происходит в течение бесконечных времен!
byJeevan
9

Я нашел хороший код в этом хранилище ,

Вот код из этого я сделал небольшие изменения в соответствии с моей потребностью в скорости :)

UIImageView + Rotate.h

#import <Foundation/Foundation.h>

@interface UIImageView (Rotate)
- (void)rotate360WithDuration:(CGFloat)duration repeatCount:(float)repeatCount;
- (void)pauseAnimations;
- (void)resumeAnimations;
- (void)stopAllAnimations;
@end

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, математика похожа на программирование, вы можете сделать то же самое несколькими способами. Это не значит, что только 1 способ верен, а другой нет. Да, у каждого способа могут быть свои плюсы и минусы, но это не значит, что вы можете задавать вопросы о каком-то методе программирования. Вы можете предложить минусы для этого метода и плюсы для вашего метода. Этот комментарий действительно оскорбительный, и вы не должны добавлять такой комментарий.
Дилип
1
Я сожалею, что обидел вас, Дилип, но написание 2 * M_PI объективно более понятно, чем ((360 * M_PI) / 180), а написание последнего демонстрирует отсутствие понимания предмета под рукой.
BreadicalMD
1
@BreadicalMD Не беспокойтесь, но это хорошее предложение, я обновил свой код. Thanx.
Дилип
9

Вот мое быстрое решение как расширение UIView. Это можно рассматривать как симуляцию поведения UIActivityIndicator в любом 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

Версия 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")
    }
}

И не забудьте позвонить startRotateв viewWillAppearне viewDidLoad.

Виктор Чой
источник
3

Вы также можете сделать анимацию того же типа, используя UIView и блоки. Вот метод расширения класса, который может вращать представление на любой угол.

- (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
источник
Будет ли этот метод (рекурсивный вызов по завершении) работать хорошо с быстро меняющейся анимацией? Например, продолжительность будет ок. 1/30 с, чтобы я мог изменять скорость вращения объекта в реальном времени, например, реагируя на прикосновения?
alecail
3

Если кто-то хотел найти решение Нейтса, но в быстром, то вот грубый быстрый перевод:

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
источник
3

для 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

Вот как я поворачиваю на 360 в правильном направлении.

[UIView animateWithDuration:1.0f delay:0.0f options:UIViewAnimationOptionRepeat|UIViewAnimationOptionCurveLinear
                     animations:^{
                         [imageIndView setTransform:CGAffineTransformRotate([imageIndView transform], M_PI-0.00001f)];
                     } completion:nil];
Павел
источник
2

Создать анимацию

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

Добавьте это к представлению как это

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

Чем отличается этот ответ? У вас будет более чистый код, если большинство ваших функций будет возвращать объекты, а не просто манипулировать некоторыми объектами здесь и там.

hfossli
источник
2

Существуют следующие способы выполнения 360-градусной анимации с помощью UIView.

Использование 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")


Вот функции расширения для UIView, которые обрабатывают запуск и остановку операций вращения:

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


Теперь используем закрытие UIView.animation :

UIView.animate(withDuration: 0.5, animations: { 
      view.transform = CGAffineTransform(rotationAngle: (CGFloat(Double.pi)) 
}) { (isAnimationComplete) in
    // Animation completed 
}
Krunal
источник
2

@ ответ барана был действительно полезным. Вот Swift-версия ответа.

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

Свифт 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
источник
как я могу остановить вращение?
user2526811
1
Как насчет того, чтобы поставить флаг? Итак, может быть, у вас есть функция для остановки анимации: var stop = false private func stopRotation() { stop = true } Тогда внутри if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja
Спасибо, я точно так же сделал. Спасибо за ваш ответ.
user2526811 21.09.16
1

Я разработал блестящий анимационный фреймворк, который поможет вам сэкономить время! С его помощью эту анимацию можно создать очень легко:

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

чтобы остановить эту анимацию просто установить nilвendlessRotater .

Если вы заинтересованы, пожалуйста, посмотрите: https://github.com/hip4yes/Animatics

Никита Архипов
источник
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()
        }
    }

ссылка

Мухаммед Заид Патан
источник
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 с использованием анимации ключевых кадров

Этот подход позволяет нам напрямую использовать 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
Мне нравится твоя, довольно чистая тоже
Николас Манзини
0

Свифт:

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

Свифт 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)
})
Warif Akhand Rishi
источник
-1

Я думаю, вам лучше добавить UIVIewкатегорию:

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

Реализация UIView (Повернуть)

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

Не используя эти методы :)

  • (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
источник
Отлично сработано. Я просто хочу прокомментировать категорию UIImageView. Все же разрешите поблагодарить вас.
Zgpeace