[ios] UIView 무한 360도 회전 애니메이션?

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다시 시작하기 전에도.



답변

나를 위해 완벽하게 작동하는 방법을 찾았습니다 ( iPhone UIImageView rotation)

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


답변

아이디어에 대한 Richard J. Ross III의 의견이지만 그의 코드가 내가 필요하지 않은 것을 발견했습니다. 에 대한 기본값 은 연속 애니메이션에서 제대로 보이지 않는 options을 제공하는 것 UIViewAnimationOptionCurveEaseInOut입니다. 또한 필요한 경우 ( 무한 이 아니라 무한한 지속 시간) 1/4 회전으로 애니메이션을 중지 하고 처음 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의 샘플 프로젝트 .


답변

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


답변

위의 Nate의 답변은 애니메이션 중지 및 시작에 이상적이며 더 나은 제어 기능을 제공합니다. 나는 왜 당신이 작동하지 않고 그의 일을하는 지 궁금했습니다. 여기서 발견 한 내용과 중단없이 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];
        }
    }];
}

전자가 애니메이션이 진행됨에 따라 저장된 결과를 반환하기 때문에 ‘CGAffineTransformMakeRotation’대신 ‘CGAffineTransformRotate’를 사용했습니다. 이렇게하면 애니메이션 중에 뷰가 점프하거나 재설정되지 않습니다.

또 다른 것은 ‘UIViewAnimationOptionRepeat’을 사용하지 않는 것입니다. 애니메이션이 반복되기 전에 애니메이션의 끝에서 뷰가 원래 위치로 되돌아 가도록 변환을 재설정하기 때문입니다. 반복 대신에 애니메이션 블록이 거의 끝나지 않기 때문에 변환이 원래 값으로 재설정되지 않도록 반복합니다.

마지막으로 360 또는 180도 (2 * M_PI 또는 M_PI) 대신 90도 (M_PI / 2) 단위로 뷰를 변환해야합니다. 변환은 사인 값과 코사인 값의 행렬 곱셈으로 발생하기 때문입니다.

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

따라서 180도 변환을 사용하는 경우 180의 코사인은 -1을 생성하여 매번 반대 방향으로 뷰를 변환합니다 (Note-Nate의 대답은 변환의 라디안 값을 M_PI로 변경하면이 문제가 발생합니다). 360도 변환은 단순히 뷰를 원래 위치에 유지하도록 요구하므로 회전이 전혀 표시되지 않습니다.


답변

이미지를 끝없이 회전시키는 것만으로도 매우 효과적이며 매우 간단합니다.

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


답변

확인 된 솔루션에서 Swift Extension을 사용한 내 기여 :

스위프트 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")
    }
}


답변

David Rysanek의 멋진 답변이 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")

        }

    }
}