Animasi rotasi 360 derajat UIView Infinite?

251

Saya mencoba memutar UIImageView360 derajat, dan telah melihat beberapa tutorial online. Saya tidak bisa membuat mereka bekerja, tanpa UIViewberhenti, atau melompat ke posisi baru.

  • Bagaimana saya bisa mencapai ini?

Hal terbaru yang saya coba adalah:

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

Tetapi jika saya menggunakan 2 * pi, itu tidak bergerak sama sekali (karena itu posisi yang sama). Jika saya mencoba melakukan hanya pi (180 derajat), itu berhasil, tetapi jika saya memanggil metode lagi, itu berputar mundur.

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

juga tidak bekerja. Ia pergi ke 180derajat, berhenti selama sepersekian detik, lalu kembali ke 0derajat sebelum mulai lagi.

Derek
sumber

Jawaban:

334

Menemukan metode (saya memodifikasinya sedikit) yang bekerja dengan baik untuk saya: iphone UIImageView rotasi

#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
sumber
25
#import <QuartzCore / QuartzCore.h>
AlBeebe
3
tambahkan QuartzCore.framework Project-> Build Phases-> Link Binaries
gjpc
9
Ini adalah kode yang tepat untuk iOS 3.0 dan di bawahnya tetapi untuk programmer yang lebih baru dan proyek baru, Apple sekarang memperingatkan pengguna menjauh dari metode ini dalam Docs & @Nate code menggunakan animasi berbasis blok yang sekarang disukai Apple
PaulWoodIII
1
@cvsguimaraes Dari dokumen: "Menentukan apakah nilai properti adalah nilai di akhir siklus berulang sebelumnya, ditambah nilai siklus berulang saat ini."
smad
12
Ini mungkin membantu seseorang, [view.layer removeAllAnimations]; untuk menghentikan animasi saat dibutuhkan.
arunit21
112

Kudos kepada Richard J. Ross III untuk ide itu, tetapi saya menemukan bahwa kodenya tidak cukup yang saya butuhkan. Default untuk options, saya percaya, adalah memberi Anda UIViewAnimationOptionCurveEaseInOut, yang tidak terlihat benar dalam animasi berkelanjutan. Juga, saya menambahkan tanda centang sehingga saya dapat menghentikan animasi saya pada perempat putaran genap jika saya membutuhkan (bukan tak terbatas , tetapi durasi tidak terbatas ), dan membuat peningkatan akselerasi naik selama 90 derajat pertama, dan melambat selama 90 derajat terakhir (setelah berhenti diminta):

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

Memperbarui

Saya menambahkan kemampuan untuk menangani permintaan untuk mulai berputar lagi ( startSpin), sementara putaran sebelumnya mereda (menyelesaikan). Contoh proyek di sini di Github .

Nate
sumber
2
Dengan menggunakan ini, saya melihat jeda singkat dalam animasi di setiap sudut PI / 2 (90 derajat) dan sedikit peningkatan dalam penggunaan CPU atas jawaban yang dipilih menggunakan CABasicAnimation. Metode CABasicAnimation menghasilkan animasi yang mulus sempurna.
Arjun Mehta
1
@Dilip, ganti M_PI / 2dengan - (M_PI / 2). (Gulir kode ke kanan untuk melihat akhir setiap baris)
Nate
1
@Dilip, saya tidak tahu berapa lama Anda menggunakan kode Anda. 0.5Detik yang sama per 90 derajat, seperti saya? Jika demikian, kode saya tidak membiarkan Anda berhenti di sebagian kecil dari rotasi 90 derajat. Hal ini memungkinkan Anda untuk berhenti di seluruh interval 90 derajat, tetapi menambahkan satu rotasi 90 derajat tambahan, "mereda". Jadi, dalam kode di atas, jika Anda menelepon stopSpinsetelah 0,35 detik, itu akan berputar selama 0,65 detik lagi, dan berhenti pada rotasi total 180 derajat. Jika Anda memiliki pertanyaan yang lebih terperinci, Anda mungkin harus membuka pertanyaan baru. Silakan tautkan ke jawaban ini.
Nate
1
Apa yang kamu lihat terjadi, @MohitJethwa? Saya memiliki aplikasi yang menggunakan ini, dan masih berfungsi untuk saya di iOS 8.1.
Nate
1
@Hemang, tentu, tapi bukan itu pertanyaannya. Saya akan mengirim pertanyaan baru jika ini yang ingin Anda lakukan.
Nate
85

Di Swift, Anda dapat menggunakan kode berikut untuk rotasi tak terbatas:

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

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

Berhenti seperti:

func stopRotatingView(view: UIView) {
    if view.layer.animationForKey(kRotationAnimationKey) != nil {
        view.layer.removeAnimationForKey(kRotationAnimationKey)
    }
}
Kadi
sumber
Apa itu kRotationAnimationKey?
Luda
Ini adalah kunci String konstan, yang membantu Anda mengidentifikasi dan mencari animasi. Ini bisa berupa String apa pun yang Anda inginkan. Dalam proses penghapusan, Anda dapat melihat bahwa animasi dicari dan dihapus oleh kunci ini.
Kadi
Apakah ada string atau sesuatu yang spesifik?
Luda
Maaf untuk jawaban terlambat, tapi ya, itu bisa berupa string apa pun.
Kadi
1
// Kunci apa saja seperti dalam jawaban
Zander Zhang
67

Jawaban Nate di atas sangat ideal untuk menghentikan dan memulai animasi dan memberikan kontrol yang lebih baik. Saya tertarik mengapa Anda tidak bekerja dan dia tidak. Saya ingin membagikan temuan saya di sini dan versi kode yang lebih sederhana yang akan menghidupkan UIView terus menerus tanpa berhenti.

Ini adalah kode yang saya gunakan,

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

Saya menggunakan 'CGAffineTransformRotate' bukan 'CGAffineTransformMakeRotation' karena yang pertama mengembalikan hasil yang disimpan sebagai hasil animasi. Ini akan mencegah lompatan atau pengaturan ulang tampilan selama animasi.

Hal lain adalah tidak menggunakan 'UIViewAnimationOptionRepeat' karena pada akhir animasi sebelum mulai berulang, itu me-reset transformasi membuat tampilan melompat kembali ke posisi semula. Alih-alih mengulang, Anda berulang sehingga transformasi tidak pernah mengatur ulang ke nilai asli karena blok animasi hampir tidak pernah berakhir.

Dan yang terakhir adalah, Anda harus mengubah tampilan dalam langkah 90 derajat (M_PI / 2) alih-alih 360 atau 180 derajat (2 * M_PI atau M_PI). Karena transformasi terjadi sebagai perkalian matriks nilai sinus dan nilai kosinus.

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

Jadi, katakanlah jika Anda menggunakan transformasi 180 derajat, cosinus dari 180 menghasilkan -1 yang membuat tampilan berubah dalam arah yang berlawanan setiap kali (Catatan-Nate juga akan memiliki masalah ini jika Anda mengubah nilai transformasi untuk M_PI). Transformasi 360 derajat hanya meminta pandangan untuk tetap di tempatnya, karenanya Anda tidak melihat rotasi sama sekali.

ram
sumber
Inti dari jawaban saya (dan jawaban Richard yang berasal dari) adalah menggunakan langkah 90 derajat , untuk menghindari beralih arah. 90 derajat juga bekerja relatif baik jika Anda ingin menghentikan rotasi , karena banyak gambar memiliki simetri 180 derajat, atau 90 derajat.
Nate
Ya, saya hanya berpikir saya akan menjelaskan mengapa ini digunakan :)
ram
2
@nik Tapi setelah pengaturan breakpoint di sana saya melihat bahwa tidak akan ada stack overflow karena blok penyelesaian dipanggil oleh sistem, saat rotateImageViewitu sudah selesai. Jadi itu tidak benar-benar rekursif dalam arti itu.
huggie
1
Jawaban bagus +1 untuk kode versi kecil dengan fungsi yang diinginkan.
Pradeep Mittal
1
Saya sangat menghargai penjelasan mengapa tidak menggunakan UIViewAnimationOptionRepeat.
Jonathan Zhan
21

Jika yang ingin Anda lakukan adalah memutar gambar tanpa henti, ini berfungsi dengan sangat baik, dan sangat sederhana:

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

Dalam pengalaman saya, ini berfungsi dengan sempurna, tetapi pastikan gambar Anda mampu diputar di sekitar pusatnya tanpa ada offset, atau animasi gambar akan "melompat" begitu berhasil masuk ke PI.

Untuk mengubah arah putaran, ubah tanda angle( angle *= -1).

Perbarui Komentar oleh @AlexPretzlav membuat saya mengunjungi kembali ini, dan saya menyadari bahwa ketika saya menulis ini, gambar yang saya putar dicerminkan di sepanjang sumbu vertikal dan horizontal, artinya gambar itu memang hanya berputar 90 derajat dan kemudian mengatur ulang, meskipun tampak seperti itu terus berputar sepanjang jalan.

Jadi, jika gambar Anda seperti milik saya, ini akan bekerja dengan baik, namun, jika gambar tersebut tidak simetris, Anda akan melihat "jepret" kembali ke orientasi semula setelah 90 derajat.

Untuk memutar gambar yang tidak simetris, Anda lebih baik dengan jawaban yang diterima.

Salah satu solusi yang kurang elegan ini, seperti terlihat di bawah, akan benar-benar memutar gambar, tetapi mungkin ada gagap yang terlihat ketika animasi dimulai kembali:

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

Anda juga bisa melakukan ini hanya dengan blok, seperti yang disarankan @ richard-j-ross-iii, tetapi Anda akan mendapatkan peringatan loop tetap karena blok tersebut menangkap dirinya sendiri:

__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
sumber
1
Ini hanya memutar 1/4 rotasi untuk saya.
Alex Pretzlav
@AlexPretzlav Pastikan Anda telah UIViewAnimationOptionRepeatmengatur animasi Anda options.
levigroker
1
Saya lakukan, itu berputar 45 °, dan kemudian loop dengan melompat kembali ke 0 ° dan berputar 45 ° lagi
Alex Pretzlav
Memperbarui jawaban saya dengan informasi baru tentang mengapa ini "berhasil"
levigroker
21

Kontribusi saya dengan Swift Extension dari solusi yang diperiksa:

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

Usang:

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
sumber
Untuk hitungan berulang, orang dapat menggunakan .infinity.
Tuan Rogers
15

Jawaban hebat David Rysanek diperbarui ke 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
sumber
bekerja sangat baik untuk saya (pada UIButton). Terima kasih!
agrippa
Saya menyukai kesederhanaan Anda
Nicolas Manzini
10

Gunakan seperempat putaran, dan tingkatkan putaran secara bertahap.

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
sumber
Saya sangat baru untuk memblokir tetapi metode ini melempar kesalahan "Jenis penunjuk blok tidak kompatibel mengirim 'void (^ const__strong ()' ke parameter tipe 'void (^) (BOOL)'. Saya mencoba mengubah metode rotasi dalam kode. dalam pertanyaan saya ke imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); Anda menggunakan, dan animasi masih memiliki sedikit keterlambatan, dan me-reset sebelum giliran berikutnya
Derek
10

Ini bekerja untuk saya:

[UIView animateWithDuration:1.0
                animations:^
{
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI);
    self.imageView.transform = CGAffineTransformMakeRotation(0);
}];
inigo333
sumber
Itu tidak terjadi untuk waktu yang tak terbatas!
Janeevan
9

Saya telah menemukan kode yang bagus di repositori ini ,

Berikut adalah kode dari itu saya telah melakukan perubahan kecil sesuai dengan kebutuhan saya akan kecepatan :)

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
sumber
@BreadicalMD, Matematika seperti pemrograman, Anda dapat melakukan hal yang sama dengan banyak cara. Itu tidak berarti bahwa hanya 1 cara yang benar dan yang lainnya tidak. Ya, mungkin ada beberapa pro dan kontra untuk segala hal, tetapi itu tidak berarti Anda dapat mempertanyakan metode pemrograman seseorang, Anda dapat menyarankan kontra untuk metode ini dan pro untuk metode Anda. Komentar ini benar-benar menyinggung, dan Anda tidak boleh menambahkan komentar seperti ini.
Dilip
1
Saya minta maaf itu menyinggung Anda Dilip, tetapi menulis 2 * M_PI secara obyektif lebih jelas daripada ((360 * M_PI) / 180), dan menulis yang terakhir menunjukkan kurangnya pemahaman tentang subjek yang ada.
BreadicalMD
1
@BreadicalMD Jangan khawatir, tapi sarannya bagus, saya telah memperbarui kode saya. Terima kasih.
Dilip
9

Ini solusi cepat saya sebagai ekstensi UIView. Ini dapat dianggap sebagai simulasi perilaku UIActivityIndicator pada UIImageView apa pun.

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
sumber
9

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

Dan ingat untuk memanggil startRotatedi viewWillAppeartidak viewDidLoad.

Victor Choy
sumber
3

Anda juga dapat melakukan jenis animasi yang sama menggunakan UIView dan blok. Berikut adalah metode ekstensi kelas yang dapat memutar tampilan dengan sudut apa pun.

- (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
sumber
Apakah metode ini (panggilan rekursif selesai) bekerja dengan baik dengan animasi yang berubah cepat? Misalnya, durasinya sekitar. 1 / 30s sehingga saya dapat memodifikasi kecepatan rotasi objek secara real time, bereaksi terhadap sentuhan misalnya?
alecail
3

Jika ada yang menginginkan solusi nates tetapi dengan cepat, berikut ini terjemahan kasarnya:

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
sumber
3

untuk 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
sumber
2

Ini adalah bagaimana saya memutar 360 ke arah yang benar.

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

Buat animasi

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

Tambahkan ke tampilan seperti ini

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

Bagaimana jawaban ini berbeda? Anda akan memiliki kode pembersih cara jika sebagian besar fungsi Anda mengembalikan objek, bukan hanya memanipulasi beberapa objek di sana-sini.

hfossli
sumber
2

Ada beberapa cara untuk melakukan animasi 360 derajat dengan UIView.

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


Berikut ini adalah fungsi ekstensi untuk UIView yang menangani operasi rotasi mulai & berhenti:

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


Sekarang menggunakan, penutupan UIView.animation :

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

Jawaban @ ram sangat membantu. Inilah versi Swift dari jawabannya.

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

Cepat 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
sumber
bagaimana saya bisa menghentikan rotasi?
user2526811
1
Bagaimana kalau Anda menaruh bendera? Jadi, mungkin Anda memiliki fungsi untuk menghentikan animasi: var stop = false private func stopRotation() { stop = true } Lalu, di dalam if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja
Terima kasih, saya juga melakukan hal yang sama. Terimakasih atas tanggapan Anda.
user2526811
1

Saya telah mengembangkan kerangka animasi mengkilap yang dapat menghemat waktu Anda! Menggunakannya, animasi ini dapat dibuat dengan sangat mudah:

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

untuk menghentikan animasi ini cukup diatur nilkeendlessRotater .

Jika Anda tertarik, silakan lihat: https://github.com/hip4yes/Animatics

Nikita Arkhipov
sumber
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
sumber
1

Cepat 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
sumber
1

Swift 5 UIView Extension menggunakan Animasi Keyframe

Pendekatan ini memungkinkan kita untuk secara langsung menggunakan 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
sumber
2
Saya suka milik Anda, cukup bersih juga
Nicolas Manzini
0

Cepat:

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
sumber
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
sumber
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
sumber
-1

Saya pikir Anda sebaiknya menambahkan UIVIewKategori:

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

Implementasi UIView (Putar)

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

Tidak menggunakan metode ini :)

  • (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
sumber
Sudah selesai dilakukan dengan baik. Saya hanya ingin mengomentari kategori UIImageView. Terima kasih semua.
Zgpeace