Search code examples
objective-ccadisplaylink

Use CADisplayLink as dynamic frequency timer


I've been using NSTimer to fade-in/fade-out some images in my app based on user-set frequency. For example, if user set frequency to 5 seconds, then every 5 seconds the following code will execute:

[UIView animateWithDuration:someInterval
                          delay:0
                        options:UIViewAnimationCurveEaseInOut
                     animations:
                        ^{
                            // UI alpha = ... code here
                        }
                     // off...
                     completion:^(BOOL finished){


                         [UIView animateWithDuration:someOtherInterval
                                               delay:yetAnotherValue
                                             options:UIViewAnimationCurveEaseInOut
                                          animations:
                                            ^{
                                                // UI alpha = ... code here
                                            }
                                          completion:nil
                          ];
                     }
     ];

(The exact code isn't important, just the overall idea of fade-in/fade-out.) However, as many on StackOverflow and various websites point out, using NSTimer results in stuttering animations because it isn't precisely tied to the frame rate. So I tried to use CADisplayLink instead:

// in viewDidAppear:
timer_count = 0;
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(checkTimer)];
displayLink.frameInterval = 1;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

-(void)checkTimer
{
    timer_count++;
    if(timer_count >= 1500)
    {
        [self doFadeInOutAnimation];
        timer_count = 0;
    }
}

This isn't having the desired effect though; the images are just displayed in very rapid succession instead of waiting to fade-in/out every 5 seconds.

Any idea what's the proper way to do it?


Solution

  • The display link is going to call your callback method checkTimer approximately every 1/30th of a second. That's all it does. The exact timing is inexact and unknown. Keeping track of how much time has elapsed since the last time it called you back, and deciding whether this means it's time to do another animation, is completely up to you. You have to use the display link's timestamp, for example, to know just how long it's been since the last callback. You aren't doing that so your timing is off. (Also you may be testing in the Simulator, where CADisplayLink doesn't work. You have to test on the device.)

    However, for a measurement as gross as "every 5 seconds", CADisplayLink is a complete waste. CADisplayLink is intended for when you yourself are animating every frame of the animation (that is, you are changing something, yourself, explicitly, every 1/30th of a second). That's not appropriate here. You don't care whether the timer is a fraction of a second off with respect to 5 seconds, so there's no big deal. Stick with your current code. If your animation is stuttering, figure out why; it has something to do with the animation, but it has nothing to do with the timer, which after all is merely saying "start" and then retiring. You have misunderstood what the various posts say; they don't say "don't use NSTimer", they say "don't use NSTimer to dictate the individual frames of an animation", and you were never doing that in the first place.