ภาพเคลื่อนไหวการหมุน 360 องศาของ UIView Infinite?


251

ฉันพยายามหมุนUIImageViewมุมมอง 360 องศาและดูบทช่วยสอนออนไลน์หลายทาง ฉันไม่สามารถทำงานได้เลยโดยไม่UIViewหยุดหรือกระโดดไปที่ตำแหน่งใหม่

  • ฉันจะบรรลุสิ่งนี้ได้อย่างไร

สิ่งล่าสุดที่ฉันได้ลองคือ:

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

แต่ถ้าฉันใช้ 2 * 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 Project-> Build Phases-> Link Binaries
gjpc

9
นี่คือรหัสที่ถูกต้องสำหรับ iOS 3.0 และต่ำกว่า แต่สำหรับโปรแกรมเมอร์ใหม่และโครงการใหม่ตอนนี้ Apple เตือนผู้ใช้ให้ออกห่างจากวิธีการเหล่านี้ในรหัส Docs & @Nate ใช้ภาพเคลื่อนไหวบล็อกที่แอปเปิลต้องการตอนนี้
PaulWoodIII

1
@cvsguimaraes จากเอกสาร: "กำหนดว่ามูลค่าของทรัพย์สินนั้นเป็นค่าในตอนท้ายของรอบการทำซ้ำก่อนหน้านี้รวมทั้งค่าของรอบการทำซ้ำปัจจุบัน"
smad

13
สิ่งนี้อาจช่วยได้บ้าง [view.layer removeAllAnimations]; เพื่อหยุดภาพเคลื่อนไหวเมื่อจำเป็น
arunit21

112

ความรุ่งโรจน์ถึง Richard J. Ross 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 องศา) และการใช้ CPU เพิ่มขึ้นเล็กน้อยในคำตอบที่เลือกโดยใช้ CABasicAnimation กระบวนการ CABasicAnimation สร้างภาพเคลื่อนไหวที่ราบรื่นไร้ที่ติ
Arjun Mehta

1
@Dilip แทนที่ด้วยM_PI / 2 - (M_PI / 2)(เลื่อนรหัสไปทางขวาเพื่อดูจุดสิ้นสุดของแต่ละบรรทัด)
เนท

1
@Dilip ฉันไม่ทราบระยะเวลาที่คุณใช้ในรหัสของคุณ เดียวกัน0.5วินาทีต่อ 90 องศาเป็นฉัน? หากเป็นเช่นนั้นรหัสของฉันจะไม่ยอมให้คุณหยุดหมุนเพียงเสี้ยววินาที จะช่วยให้คุณหยุดที่ช่วงเวลาทั้งหมด 90 องศา แต่เพิ่มการหมุน 90 องศาหนึ่ง "เพิ่มออก" ดังนั้นในรหัสข้างต้นถ้าคุณโทรstopSpinหลังจาก 0.35 วินาทีมันจะหมุนอีก 0.65 วินาทีและหยุดที่ 180 องศาการหมุนทั้งหมด หากคุณมีคำถามเพิ่มเติมคุณควรเปิดคำถามใหม่ รู้สึกอิสระที่จะเชื่อมโยงกับคำตอบนี้
เนท

1
@MohitJethwa เกิดอะไรขึ้น? ฉันมีแอพที่ใช้สิ่งนี้และมันยังใช้ได้กับฉันใน iOS 8.1
เนท

1
@ เฮงแน่นอน แต่นั่นไม่ใช่คำถามนี้ ฉันจะโพสต์คำถามใหม่ถ้านี่คือสิ่งที่คุณพยายามจะทำ
เนท

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 คืออะไร
Luda

มันเป็นคีย์สตริงคงที่ที่ช่วยให้คุณระบุและค้นหาภาพเคลื่อนไหว มันสามารถเป็นสตริงใด ๆ ที่คุณต้องการ ในกระบวนการลบคุณจะเห็นว่าภาพเคลื่อนไหวนั้นถูกค้นหาและลบโดยปุ่มนี้
Kádi

มันเป็นสตริงหรืออะไรที่เฉพาะเจาะจง?
Luda

ขออภัยสำหรับคำตอบที่ล่าช้า แต่ใช่มันอาจเป็นสตริงใด ๆ
Kádi

1
// กุญแจใด ๆ ในคำตอบ
Zander Zhang

67

คำตอบของ 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];
        }
    }];
}

ฉันใช้ '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 จะทำให้การแปลงมุมมองไปในทิศทางตรงกันข้ามในแต่ละครั้ง (คำตอบของ Note-Nate จะมีปัญหานี้เช่นกันถ้าคุณเปลี่ยนค่าเรเดียนของการแปลงเป็น M_PI) การเปลี่ยนแปลงแบบ 360 องศาเพียงแค่ขอให้มุมมองอยู่ในตำแหน่งเดิมดังนั้นคุณจะไม่เห็นการหมุนใด ๆ เลย


จุดทั้งหมดของคำตอบของฉัน (และคำตอบของริชาร์ดนั้นมาจาก) คือการใช้ 90ขั้นตอนเพื่อหลีกเลี่ยงการเปลี่ยนทิศทาง 90 องศาใช้งานได้ค่อนข้างดีหากคุณไม่ต้องการหยุดหมุนเนื่องจากภาพหลายภาพมี 180 องศาหรือสมมาตร 90 องศา
เนท

ใช่ฉันคิดว่าฉันจะอธิบายว่าทำไมมันจะถูกใช้ :)
ram

2
@nik แต่หลังจากตั้งค่าเบรกพอยต์ในนั้นฉันเห็นว่าจะไม่มีการล้นสแต็คเนื่องจากระบบบล็อกเสร็จสมบูรณ์ถูกเรียกโดยระบบในเวลานั้นrotateImageViewได้เสร็จสิ้นแล้ว ดังนั้นมันจึงไม่เรียกซ้ำในความเป็นจริง
ฮักกี้

1
คำตอบที่ดี +1 สำหรับโค้ดขนาดเล็กที่มีฟังก์ชั่นที่ต้องการ
Pradeep Mittal

1
ฉันชื่นชมคำอธิบายว่าทำไมไม่ใช้ UIViewAnimationOptionRepeat
Jonathan Zhan

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 ของการหมุนสำหรับฉันเท่านั้น
Alex Pretzlav

@AlexPretzlav แน่ใจว่าคุณมีการตั้งค่าในการเคลื่อนไหวของคุณUIViewAnimationOptionRepeat options
levigroker

1
ฉันทำมันหมุน 45 °แล้ววนซ้ำด้วยการกระโดดกลับไปที่ 0 °และหมุน 45 °อีกครั้ง
Alex Pretzlav

อัปเดตคำตอบของฉันพร้อมกับข้อมูลใหม่เกี่ยวกับสาเหตุที่ทำให้ "ทำงาน"
levigroker

21

การมีส่วนร่วมของฉันกับ Swift Extension จากโซลูชันที่เลือก:

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สำหรับการนับซ้ำหนึ่งสามารถใช้
Mr Rogers

15

คำตอบที่ยอดเยี่ยมของ 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")

        }   

    }
}

ทำงานได้ดีมากสำหรับฉัน (ใน UIButton) ขอบคุณ!
agrippa

ฉันชอบความเรียบง่ายของคุณ
Nicolas Manzini

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, คณิตศาสตร์เปรียบเสมือนการเขียนโปรแกรมคุณสามารถทำสิ่งเดียวกันได้หลายวิธี ไม่ได้หมายความว่ามีทางเดียวเท่านั้นที่ถูกต้องและอื่น ๆ ไม่ได้ ใช่อาจมีข้อดีและข้อเสียสำหรับทุก ๆ ทาง แต่นั่นไม่ได้หมายความว่าคุณสามารถถามวิธีการเขียนโปรแกรม someones คุณสามารถแนะนำข้อเสียสำหรับวิธีการนี้และข้อดีสำหรับวิธีการของคุณ ความคิดเห็นนี้ไม่เหมาะสมและคุณไม่ควรเพิ่มความคิดเห็นเช่นนี้
Dilip

1
ฉันขอโทษที่ทำให้คุณขุ่นเคือง แต่การเขียน 2 * M_PI นั้นชัดเจนกว่า ((360 * M_PI) / 180) อย่างมีนัยสำคัญและการเขียนหลังนั้นแสดงให้เห็นถึงการขาดความเข้าใจในเรื่องที่อยู่ในมือ
BreadicalMD

1
@BreadicalMD ไม่ต้องกังวล แต่เป็นคำแนะนำที่ดีฉันได้อัปเดตโค้ด Thanx
Dilip

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ในไม่ได้อยู่ในviewWillAppearviewDidLoad


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 / 30s เพื่อให้ฉันสามารถปรับเปลี่ยนความเร็วการหมุนของวัตถุแบบเรียลไทม์ตอบสนองต่อการสัมผัสเช่น?
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

คำตอบของ @ ram มีประโยชน์จริงๆ นี่คือคำตอบเวอร์ชั่นสวิฟท์

สวิฟท์ 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
คุณใส่ธงได้อย่างไร? ดังนั้นบางทีคุณอาจมี func สำหรับหยุดการเคลื่อนไหว: var stop = false private func stopRotation() { stop = true } จากนั้นภายในif finished {...}:if finished { if stop { return} self.rotateImageView() }
yoninja

ขอบคุณฉันทำเช่นเดียวกัน ขอบคุณสำหรับคำตอบของคุณ
user2526811

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

ที่จะหยุดการเคลื่อนไหวนี้ก็ตั้งไปnilendlessRotater

หากคุณสนใจโปรดดู: 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 Extension โดยใช้ Keyframe Animation

วิธีการนี้ช่วยให้เราสามารถใช้ 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
ฉันชอบคุณสะอาดเหมือนกัน
Nicolas Manzini

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.