UIView Infinite xoay 360 độ hoạt hình?


251

Tôi đang cố gắng xoay UIImageView360 độ, và đã xem một số hướng dẫn trực tuyến. Tôi có thể khiến không ai trong số họ làm việc, mà không UIViewdừng lại, hoặc nhảy sang một vị trí mới.

  • Làm thế nào tôi có thể đạt được điều này?

Điều mới nhất tôi đã thử là:

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

Nhưng nếu tôi sử dụng 2 * pi, nó hoàn toàn không di chuyển (vì nó cùng một vị trí). Nếu tôi cố gắng chỉ làm pi (180 độ), nó hoạt động, nhưng nếu tôi gọi lại phương thức, nó sẽ quay ngược lại.

CHỈNH SỬA :

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

cũng không hoạt động. Nó chuyển sang 180độ, tạm dừng trong một giây, sau đó đặt lại về 0độ trước khi bắt đầu lại.

Câu trả lời:


334

Tìm thấy một phương thức (tôi đã sửa đổi một chút) hoạt động hoàn hảo với tôi: xoay vòng 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
thêm Dự án QuartzCore.framework-> Xây dựng giai đoạn-> Liên kết nhị phân
gjpc

9
Đây là mã phù hợp cho iOS 3.0 trở xuống nhưng đối với các lập trình viên mới hơn và các dự án mới, Apple hiện cảnh báo người dùng tránh xa các phương thức này trong mã Docs & @Nate sử dụng hình động dựa trên khối mà Apple hiện thích
PaulWoodIII

1
@cvsguimaraes Từ tài liệu: "Xác định xem giá trị của thuộc tính có phải là giá trị ở cuối chu kỳ lặp lại trước đó hay không, cộng với giá trị của chu kỳ lặp lại hiện tại."
smad

12
Điều này có thể giúp một số người, [view.layer remove ALLAnimations]; dừng hoạt hình khi cần.
arunit21

112

Kudos cho Richard J. Ross III cho ý tưởng, nhưng tôi thấy rằng mã của anh ta không hoàn toàn là những gì tôi cần. optionsTôi tin rằng mặc định là cung cấp cho bạn UIViewAnimationOptionCurveEaseInOut, cái nhìn không đúng trong một hình ảnh động liên tục. Ngoài ra, tôi đã thêm một kiểm tra để tôi có thể dừng hoạt hình của mình ở một phần tư chẵn nếu tôi cần (không phải là vô hạn , nhưng trong thời gian không xác định ), và tăng tốc tăng tốc trong 90 độ đầu tiên và giảm tốc trong 90 độ cuối cùng (sau khi dừng được yêu cầu):

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

Cập nhật

Tôi đã thêm khả năng xử lý các yêu cầu để bắt đầu quay lại ( startSpin), trong khi vòng quay trước đó đang cuộn xuống (hoàn thành). Dự án mẫu ở đây trên Github .


2
Sử dụng điều này, tôi nhận thấy một khoảng dừng ngắn trong hoạt hình ở mỗi góc PI / 2 (90 độ) và mức tăng sử dụng CPU so với câu trả lời đã chọn bằng CABasicAnimation. Phương pháp CABasicAnimation tạo ra một hình ảnh động hoàn hảo.
Arjun Mehta

1
@Dilip, thay thế M_PI / 2bằng - (M_PI / 2). (Di chuyển mã sang bên phải để xem cuối mỗi dòng)
Nate

1
@Dilip, tôi không biết thời lượng bạn sử dụng trong mã của mình. Cùng một 0.5giây trên 90 độ, như tôi? Nếu vậy, mã của tôi không cho phép bạn dừng lại ở một phần của góc xoay 90 độ. Nó cho phép bạn dừng lại ở toàn bộ số lượng khoảng 90 độ, nhưng thêm một góc quay 90 độ nữa, "nới lỏng". Vì vậy, trong đoạn mã trên, nếu bạn gọi stopSpinsau 0,35 giây, nó sẽ quay thêm 0,65 giây nữa và dừng ở góc quay tổng 180 độ. Nếu bạn có câu hỏi chi tiết hơn, có lẽ bạn nên mở một câu hỏi mới. Hãy liên kết với câu trả lời này.
Nate

1
Bạn đang thấy điều gì xảy ra, @MohitJethwa? Tôi có một ứng dụng sử dụng ứng dụng này và nó vẫn hoạt động với tôi trên iOS 8.1.
Nate

1
@Hemang, chắc chắn, nhưng đó không phải là câu hỏi này. Tôi sẽ đăng một câu hỏi mới nếu đây là những gì bạn đang cố gắng làm.
Nate

85

Trong Swift, bạn có thể sử dụng mã sau đây để xoay vô hạn:

Swift 4

extension UIView {
    private static let kRotationAnimationKey = "rotationanimationkey"

    func rotate(duration: Double = 1) {
        if layer.animation(forKey: UIView.kRotationAnimationKey) == nil {
            let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")

            rotationAnimation.fromValue = 0.0
            rotationAnimation.toValue = Float.pi * 2.0
            rotationAnimation.duration = duration
            rotationAnimation.repeatCount = Float.infinity

            layer.add(rotationAnimation, forKey: UIView.kRotationAnimationKey)
        }
    }

    func stopRotating() {
        if layer.animation(forKey: UIView.kRotationAnimationKey) != nil {
            layer.removeAnimation(forKey: UIView.kRotationAnimationKey)
        }
    }
}

Swift 3

let kRotationAnimationKey = "com.myapplication.rotationanimationkey" // Any key

func rotateView(view: UIView, duration: Double = 1) {
    if view.layer.animationForKey(kRotationAnimationKey) == nil {
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation")

        rotationAnimation.fromValue = 0.0
        rotationAnimation.toValue = Float(M_PI * 2.0)
        rotationAnimation.duration = duration
        rotationAnimation.repeatCount = Float.infinity

        view.layer.addAnimation(rotationAnimation, forKey: kRotationAnimationKey)
    }
}

Dừng lại giống như:

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

Các kRotationAnimationKey là gì?
Luda

Đó là một khóa Chuỗi không đổi, giúp bạn xác định và tìm kiếm hình ảnh động. Nó có thể là bất kỳ Chuỗi nào bạn muốn. Trong quá trình loại bỏ, bạn có thể thấy rằng hình ảnh động được tìm kiếm và bị xóa bởi phím này.
Kádi

Có bất kỳ chuỗi hoặc một cái gì đó cụ thể?
Luda

Xin lỗi vì trả lời muộn, nhưng vâng, nó có thể là bất kỳ chuỗi nào.
Kádi

1
// Bất kỳ khóa nào như trong câu trả lời
Zander Zhang

67

Câu trả lời của Nate ở trên là lý tưởng cho việc dừng và bắt đầu hoạt hình và kiểm soát tốt hơn. Tôi đã tò mò tại sao bạn không làm việc và anh ấy làm. Tôi muốn chia sẻ những phát hiện của tôi ở đây và một phiên bản mã đơn giản hơn có thể làm động một UIView liên tục mà không bị đình trệ.

Đây là mã tôi đã sử dụng,

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

Tôi đã sử dụng 'CGAffineTransformRotate' thay vì 'CGAffineTransformMakeRotation' vì trước đây trả về kết quả được lưu khi tiến hành hoạt hình. Điều này sẽ ngăn việc nhảy hoặc đặt lại chế độ xem trong hình ảnh động.

Một điều nữa là không sử dụng 'UIViewAnimationOptionRepeat' bởi vì ở phần cuối của hình ảnh động trước khi bắt đầu lặp lại, nó đặt lại biến đổi làm cho chế độ xem quay trở lại vị trí ban đầu. Thay vì lặp lại, bạn lặp lại để biến đổi không bao giờ được đặt lại về giá trị ban đầu vì khối hoạt hình hầu như không bao giờ kết thúc.

Và điều cuối cùng là, bạn phải chuyển đổi chế độ xem theo các bước 90 độ (M_PI / 2) thay vì 360 hoặc 180 độ (2 * M_PI hoặc M_PI). Bởi vì phép biến đổi xảy ra như một phép nhân ma trận của các giá trị sin và cos.

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

Vì vậy, giả sử nếu bạn sử dụng phép biến đổi 180 độ, cosin của 180 mang lại -1 làm cho khung nhìn biến đổi theo hướng ngược lại mỗi lần (câu trả lời của Note-Nate cũng sẽ có vấn đề này nếu bạn thay đổi giá trị radian của phép biến đổi thành M_PI). Chuyển đổi 360 độ chỉ đơn giản là yêu cầu chế độ xem giữ nguyên vị trí của nó, do đó bạn hoàn toàn không thấy bất kỳ góc quay nào.


Toàn bộ câu trả lời của tôi (và câu trả lời của Richard mà nó bắt nguồn từ) là sử dụng các bước 90 độ , để tránh chuyển hướng. 90 độ cũng hoạt động tương đối tốt nếu bạn muốn dừng quay , vì nhiều hình ảnh có đối xứng 180 độ hoặc 90 độ.
Nate

Vâng, tôi chỉ nghĩ rằng tôi sẽ giải thích lý do tại sao nó đang được sử dụng :)
ram

2
@nik Nhưng sau khi thiết lập điểm dừng ở đó, tôi thấy rằng sẽ không có tràn ngăn xếp vì khối hoàn thành được gọi bởi hệ thống, đến lúc đó rotateImageViewđã kết thúc. Vì vậy, nó không thực sự đệ quy theo nghĩa đó.
huggie

1
Câu trả lời tốt +1 cho một phiên bản mã nhỏ với chức năng mong muốn.
Pradeep Găngal

1
Tôi thực sự đánh giá cao lời giải thích tại sao không sử dụng UIViewAnimationOptionRepeat.
Jonathan Zhan

21

Nếu tất cả những gì bạn muốn làm là xoay hình ảnh vô tận, thì điều này hoạt động khá tốt và rất đơn giản:

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

Theo kinh nghiệm của tôi, điều này hoạt động hoàn hảo, nhưng hãy chắc chắn rằng hình ảnh của bạn có khả năng xoay quanh tâm của nó mà không có bất kỳ sự bù trừ nào, hoặc hình ảnh động sẽ "nhảy" khi nó quay xung quanh PI.

Để thay đổi hướng quay, thay đổi dấu của angle( angle *= -1).

Cập nhật Nhận xét của @AlexPretzlav khiến tôi phải xem lại điều này và tôi nhận ra rằng khi tôi viết hình ảnh này, tôi đang quay được nhân đôi dọc theo trục dọc và trục ngang, nghĩa là hình ảnh thực sự chỉ xoay 90 độ và sau đó đặt lại, mặc dù nó trông giống như nó đang tiếp tục xoay vòng

Vì vậy, nếu hình ảnh của bạn giống như của tôi, điều này sẽ hoạt động rất tốt, tuy nhiên, nếu hình ảnh không đối xứng, bạn sẽ nhận thấy "snap" trở lại hướng ban đầu sau 90 độ.

Để xoay hình ảnh không đối xứng, tốt hơn hết bạn nên trả lời bằng câu trả lời được chấp nhận.

Một trong những giải pháp kém thanh lịch này, được nhìn thấy bên dưới, sẽ thực sự xoay hình ảnh, nhưng có thể có một sự nhầm lẫn đáng chú ý khi hoạt hình được khởi động lại:

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

Bạn cũng có thể làm điều này chỉ với các khối, như @ richard-j-ross-iii gợi ý, nhưng bạn sẽ nhận được cảnh báo vòng lặp giữ lại vì khối đang tự chụp:

__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
Điều này chỉ xoay 1/4 vòng quay cho tôi.
Alex Pretzlav

@AlexPretzlav Hãy chắc chắn rằng bạn đã UIViewAnimationOptionRepeatthiết lập trong hoạt hình của mình options.
levigroker

1
Tôi đã làm, nó quay 45 °, và sau đó lặp lại bằng cách nhảy trở lại 0 ° và quay lại 45 °
Alex Pretzlav

Đã cập nhật câu trả lời của tôi với thông tin mới về lý do tại sao "hoạt động" này
levigroker

21

Đóng góp của tôi với Tiện ích mở rộng Swift từ giải pháp đã kiểm tra:

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

Không dùng nữa

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

Đối với số lượng lặp lại, người ta có thể sử dụng .infinity.
Ông Rogers

15

Câu trả lời tuyệt vời của David Rysanek được cập nhật lên 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")

        }   

    }
}

làm việc rất tốt cho tôi (trên một UIButton). Cảm ơn bạn!
agrippa

tôi thích sự đơn giản của bạn
Nicolas Manzini

10

Sử dụng lượt quý và tăng lượt tăng dần.

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

Tôi rất mới đối với các khối nhưng phương thức này đưa ra lỗi "Loại con trỏ khối không tương thích gửi 'void (^ const__strong ()' đến tham số của loại 'void (^) (BOOL)'. Tôi đã cố gắng thay đổi phương thức xoay vòng của mình trong mã trong câu hỏi của tôi với imageToMove.transform = CGAffineTransformRotate (imageToMove.transform, M_PI / 2); bạn đã sử dụng và hoạt hình vẫn có độ trễ nhẹ và đặt lại trước lượt tiếp theo.
Derek

10

Điều này đã làm việc cho tôi:

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

Nó không xảy ra trong thời gian vô tận!
byJeevan

9

Tôi đã tìm thấy mã đẹp trong kho lưu trữ này ,

Đây là mã từ nó tôi đã thực hiện những thay đổi nhỏ theo nhu cầu tốc độ của mình :)

UIImageView + Xoay.h

#import <Foundation/Foundation.h>

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

UIImageView + Xoay.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, Toán học giống như lập trình, Bạn có thể làm điều tương tự với nhiều cách. Điều đó không có nghĩa là chỉ có 1 cách là đúng còn cách khác thì không. Vâng, có thể có một số ưu và nhược điểm cho mọi cách, nhưng điều đó không có nghĩa là bạn có thể đặt câu hỏi về phương pháp lập trình của ai đó, Bạn có thể đề xuất nhược điểm cho phương pháp này và ưu điểm cho phương pháp của bạn. Nhận xét này thực sự gây khó chịu và bạn không nên thêm nhận xét như thế này.
Dilip

1
Tôi xin lỗi vì nó đã xúc phạm Dilip của bạn, nhưng viết 2 * M_PI rõ ràng khách quan hơn ((360 * M_PI) / 180), và viết cái sau chứng tỏ sự thiếu hiểu biết về chủ đề này.
BreadicalMD

1
@BreadicalMD Không phải lo lắng, nhưng gợi ý tốt, tôi đã cập nhật mã của mình. Thanx.
Dilip

9

Đây là giải pháp nhanh chóng của tôi như là một phần mở rộng UIView. Nó có thể được coi là một mô phỏng của hành vi UIActivityTheicator trên bất kỳ UIImageView nào.

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

Phiên bản 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")
    }
}

Và hãy nhớ để gọi startRotatetrong viewWillAppearkhông viewDidLoad.


3

Bạn cũng có thể thực hiện cùng loại hoạt hình bằng cách sử dụng UIView và các khối. Đây là một phương thức mở rộng lớp có thể xoay chế độ xem theo bất kỳ góc nào.

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

Phương pháp này (cuộc gọi đệ quy khi hoàn thành) có hoạt động tốt với hình ảnh động thay đổi nhanh không? Ví dụ, thời lượng sẽ xấp xỉ. 1/30 giây để tôi có thể sửa đổi tốc độ quay của đối tượng trong thời gian thực, phản ứng với các lần chạm chẳng hạn?
alecail

3

Nếu bất cứ ai muốn giải pháp của nates nhưng nhanh chóng, thì đây là một bản dịch nhanh chóng:

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

cho 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

Đây là cách tôi xoay 360 đúng hướng.

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

2

Tạo hình ảnh động

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

Thêm nó vào một khung nhìn như thế này

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

Câu trả lời này khác nhau như thế nào? Bạn sẽ có mã sạch hơn nếu hầu hết các hàm của bạn trả về các đối tượng thay vì chỉ thao tác một số đối tượng ở đây và ở đó.


2

Có nhiều cách khác nhau để thực hiện hoạt hình 360 độ với UIView.

Sử dụng 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")


Đây là một chức năng mở rộng cho UIView xử lý các hoạt động bắt đầu và dừng quay:

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


Hiện đang sử dụng, đóng UIView.animation :

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

2

Câu trả lời của @ ram thực sự hữu ích. Đây là phiên bản Swift của câu trả lời.

Swift 2

private func rotateImageView() {

    UIView.animateWithDuration(1, delay: 0, options: UIViewAnimationOptions.CurveLinear, animations: { () -> Void in
        self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, CGFloat(M_PI_2))
        }) { (finished) -> Void in
            if finished {
                self.rotateImageView()
            }
    }
}

Swift 3,4,5

private func rotateImageView() {

    UIView.animate(withDuration: 1, delay: 0, options: UIView.AnimationOptions.curveLinear, animations: { () -> Void in
        self.imageView.transform = self.imageView.transform.rotated(by: .pi / 2)
    }) { (finished) -> Void in
        if finished {
            self.rotateImageView()
        }
    }
}

Làm thế nào tôi có thể ngừng quay?
dùng2526811

1
Làm thế nào về bạn đặt một lá cờ? Vì vậy, có thể bạn có một chức năng để dừng hoạt hình: var stop = false private func stopRotation() { stop = true } Sau đó, bên trong if finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja

Cảm ơn bạn, tôi chính xác đã làm như vậy. Cám ơn phản hồi của bạn.
dùng2526811

1

Tôi đã phát triển một khung hoạt hình sáng bóng có thể giúp bạn tiết kiệm thời gian! Sử dụng nó hoạt hình này có thể được tạo ra rất dễ dàng:

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

để dừng hoạt hình này chỉ cần đặt nilthành endlessRotater.

Nếu bạn quan tâm, vui lòng xem: 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()
        }
    }

Tham chiếu


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

Tiện ích mở rộng Swift 5 UIView bằng Ảnh động Keyframe

Cách tiếp cận này cho phép chúng tôi sử dụng trực tiếp 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
Tôi thích của bạn, cũng khá sạch sẽ
Nicolas Manzini

0

Swift:

func runSpinAnimationOnView(view:UIView , duration:Float, rotations:Double, repeatt:Float ) ->()
    {
        let rotationAnimation=CABasicAnimation();

        rotationAnimation.keyPath="transform.rotation.z"

        let toValue = M_PI * 2.0 * rotations ;


        // passing it a float
        let someInterval = CFTimeInterval(duration)

        rotationAnimation.toValue=toValue;
        rotationAnimation.duration=someInterval;
        rotationAnimation.cumulative=true;
        rotationAnimation.repeatCount=repeatt;
        view.layer.addAnimation(rotationAnimation, forKey: "rotationAnimation")


    }

0

Swift 3:

 var rotationAnimation = CABasicAnimation()
     rotationAnimation = CABasicAnimation.init(keyPath: "transform.rotation.z")
     rotationAnimation.toValue = NSNumber(value: (M_PI * 2.0))
     rotationAnimation.duration = 2.0
     rotationAnimation.isCumulative = true
     rotationAnimation.repeatCount = 10.0
     view.layer.add(rotationAnimation, forKey: "rotationAnimation")

0
let val = CGFloat(M_PI_2)

UIView.animate(withDuration: 1, delay: 0, options: [.repeat, .curveLinear], animations: {
        self.viewToRotate.transform = self.viewToRotate.transform.rotated(by: val)
})

-1

Tôi nghĩ bạn nên thêm một UIVIewDanh mục:

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

Thực hiện UIView (Xoay vòng)

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

Không sử dụng phương pháp này :)

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

Làm tốt. Tôi chỉ muốn bình luận về thể loại UIImageView. Cảm ơn tất cả các bạn như nhau.
Zgpeace
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.