UIView Нескінченна анімація обертання на 360 градусів?


251

Я намагаюся повернути на UIImageView360 градусів і переглянув кілька навчальних посібників в Інтернеті. Я не міг змусити жодного з них працювати, не UIViewзупиняючись і не стрибуючи на нову посаду.

  • Як я можу цього досягти?

Останнє, що я спробував:

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

Але якщо я використовую 2 * pi, він взагалі не рухається (оскільки це одне і те ж положення). Якщо я спробую зробити просто pi (180 градусів), він працює, але якщо я знову зателефоную за методом, він обертається назад.

Редагувати :

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

теж не працює. Він переходить до 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-> Фази збірки-> Бінарні файли посилань
gjpc

9
Це правильний код для iOS 3.0 і нижче, але для нових програмістів та нових проектів Apple тепер попереджає користувачів від цих методів у коді Docs & @Nate, використовуючи блокові анімації, які зараз віддає перевагу Apple
PaulWoodIII

1
@cvsguimaraes З doc: "Визначає, чи значення властивості є значенням наприкінці попереднього циклу повторення плюс значення поточного циклу повторень."
smad

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 .


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 градусів. Якщо у вас є більш детальні запитання, вам, ймовірно, слід відкрити нове запитання. Сміливо посилайтесь на цю відповідь.
Нейт

1
Що ти бачиш, @MohitJethwa? У мене є додаток, яке використовує це, і воно все ще працює для мене на iOS 8.1.
Нейт

1
@Hemang, звичайно, але це не таке питання. Я б розмістив нове запитання, якщо це те, що ви намагаєтеся зробити.
Нейт

85

У 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?
Люда,

Це постійна клавіша String, яка допомагає вам ідентифікувати та шукати анімацію. Це може бути будь-яка струна. У процесі видалення ви можете бачити, що за допомогою цього ключа анімація шукається та видаляється.
Каді

Це якась струна чи щось конкретне?
Люда

Вибачте за пізню відповідь, але так, це може бути будь-який рядок.
Каді

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вже завершена. Тож насправді це не рекурсивно в цьому сенсі.
huggie

1
Хороша відповідь +1 для невеликої версії коду з потрібною функціональністю.
Pradeep Mittal

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

1
Це тільки для мене оберталося на 1/4 обертання.
Олексій Прецлав

@AlexPretzlav Будьте впевнені, що ви UIViewAnimationOptionRepeatвстановили в анімації options.
levigroker

1
Я зробив це, він обертається на 45 °, а потім петлі, відскочивши назад до 0 ° і знову повернувшись на 45 °
Олексій Прецлав

Оновив свою відповідь новою інформацією про те, чому це "спрацювало"
levigroker

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

Для повторного підрахунку можна скористатися .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);

Я дуже новий в блоках, але цей метод кидає помилку "Несумісні типи вказівників блоку, що надсилають '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);
}];

Це не відбувається нескінченно!
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
Вибачте, що образив вас, Діліп, але написання 2 * M_PI об'єктивно зрозуміліше, ніж ((360 * M_PI) / 180), а написання останнього свідчить про нерозуміння предмета.
BreadicalMD

1
@BreadicalMD Не хвилюйтесь, але гарна пропозиція, я оновив свій код. Дякую.
Діліп

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

Чи добре би цей метод (рекурсивний виклик після завершення) добре працював із анімацією, що швидко змінюється? Наприклад, тривалість буде приблизно. 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()
    }
}

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

Чим відрізняється ця відповідь? У вас буде спосіб чистішого коду, якщо більшість ваших функцій повертає об’єкти, а не просто маніпулювати деякими об'єктами тут і там.


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 
}

2

@ Баранячий відповідь був дуже корисним. Ось швидка версія відповіді.

Швидкий 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()
        }
    }
}

як я можу зупинити обертання?
користувач2526811

1
Як ви ставите прапор? Тож, можливо, у вас є функція зупинки анімації: var stop = false private func stopRotation() { stop = true } Потім, всередині if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja

Дякую, я точно так само зробив. Дякую за Вашу відповідь.
користувач2526811

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

Швидкий 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 за допомогою анімації Keyframe

Такий підхід дозволяє нам безпосередньо використовувати 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)
})

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

Молодці. Я просто хочу прокоментувати категорію UIImageView. Дякую вам все одно.
Zgpeace
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.