2

I've been advised to try CADisplayLink to rotate a UIIMageView. I'm building this app using Storyboard.

I need to start and stop the animation, when the screen is tapped. I also need to track the angle at which the rotation stopped when the screen was tapped.

I've found this tutorial HERE but it's not exactly helpful to me.

I've also found a couple of small examples (here's one), but I couldn't get it to actually animate. From what I took from the above example, my image rotated the once, then just stopped.

Other that that, i really can't find a good tutorial or example of how to use CADisplayLink to rotate my UIImageView continuously, until tapped, then continue to rotate when tapped again and finally storing the angle of rotation.

To give you an idea of what I'm trying to achieve, it's basically a clock face, and as a clock hand rotates, you can start and stop it by tapping, and retrieve the angle.

Does anyone know a good example? or maybe could help me out here. Thanks.

UPDATE / EDIT

Using Rob's awesome Obj-C answer below in my app works perfectly. Incase anyone is interested, this is how I track the angle of rotation and display it to the screen:

Every time the HandleDisplayLink is called, I called this method: [self calculateDisplayAngle];

The method is simple:

 - (void)calculateDisplayAngle{
         currentRotationAngle = self.percent*360; //360 because I changed kDuration = 1;
         self.AngleLabel.text = [NSString stringWithFormat:@"Angle: %.2f",currentRotationAngle];
 }
Reanimation
  • 3,151
  • 10
  • 50
  • 88

1 Answers1

3

You can just have properties to keep track of the state of the rotation so that when you stop it, it can pick up where it left off. For example:

@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()

    startDisplayLink()
}

@IBAction func handleTapGesture(sender: UITapGestureRecognizer) {
    if displayLink != nil {
        stopDisplayLink()
    } else {
        startDisplayLink()
    }
}

private let duration = 2.5
private var percent = 0.0
private var displayLink: CADisplayLink?
private var start: CFAbsoluteTime?

func startDisplayLink() {
    displayLink = CADisplayLink(target: self, selector: "handleDisplayLink:")
    start = CFAbsoluteTimeGetCurrent() + (1.0 - percent) * duration
    displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes)
}

func stopDisplayLink() {
    displayLink?.invalidate()
    displayLink = nil
}

func handleDisplayLink(displayLink: CADisplayLink) {
    let elapsed = CFAbsoluteTimeGetCurrent() - start!
    percent = (elapsed % duration) / duration
    imageView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI * 2.0 * percent))
}

or in Objective-C:

static CGFloat const kDuration = 2.5;

@interface ViewController2 ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@property (nonatomic, strong) CADisplayLink *displayLink;
@property (nonatomic) CFAbsoluteTime startTime;
@property (nonatomic) CGFloat percent;

@end

@implementation ViewController2

- (IBAction)handleTapGesture:(UITapGestureRecognizer *)sender {
    if (self.displayLink) {
        [self stopDisplayLink];
    } else {
        [self startDisplayLink];
    }
}

- (void)viewDidLoad {
    [super viewDidLoad];

    [self startDisplayLink];
    // Do any additional setup after loading the view.
}


- (void)startDisplayLink {
    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
    self.startTime = CFAbsoluteTimeGetCurrent() + (1.0 - self.percent) * kDuration;
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}

- (void)stopDisplayLink {
    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)handleDisplayLink:(CADisplayLink *)displayLink {
    CFAbsoluteTime elapsed = CFAbsoluteTimeGetCurrent() - self.startTime;
    self.percent = elapsed / kDuration - floor(elapsed / kDuration);
    self.imageView.transform = CGAffineTransformMakeRotation(M_PI * 2.0 * self.percent);
}

@end
Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Looks like it makes sense. So does it do. 360 rotation every 2.5 seconds? And rotate infinitely until stopped? Will this work if my UIImageView is on my Storyboard and linked to my `.h`? Sorry, this is my first time with this sort of thing. Also, I've not seen question mark syntax before in this context. It's late here so I will look at this tomorrow properly. – Reanimation Dec 03 '15 at 00:21
  • Oh wait, is this swift? – Reanimation Dec 03 '15 at 00:22
  • Oops, my apologise for that. Thanks for taking the time to post, I do appreciate it. I will try and translate it to ObjC tomorrow. – Reanimation Dec 03 '15 at 00:24
  • The translation is pretty simple. See revised answer above. – Rob Dec 03 '15 at 00:42
  • Thank you so much. I can't wait to give it a try tomorrow (it's almost 1am here). I will let you know how it goes (: thanks again. – Reanimation Dec 03 '15 at 00:45
  • I just want to say thanks again. It's now the next day and I've implemented it into my app and it's flawless. I'm still not sure why I couldn't get it to work... I must have been missing something I still can't see. But yours is great. Thank you so much :D I will update my question to include how I track the angle incase anyone is interested. Have a good day. – Reanimation Dec 03 '15 at 13:38