I'm using CADisplayLink
in my iPhone app.
Here is the relevant code:
SMPTELink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onTick)];
SMPTELink.frameInterval = 2;//30fps 60/n = fps
[SMPTELink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
onTick is thus called every frame of 30FPS
(1/30th of a second). This works GREAT on iOS6
+ - does exactly what I need. However, when I ran my app on an iPhone 4s running iOS5.1, the onTick method ran slightly slower than with the iOS6 counterpart. Almost like it was running it 29FPS
. After a little bit, it was out of sync with the iOS6 iPhone 5.
The code in the onTick method is not time consuming (that was one of my thoughts...), and it's not the iPhone because the app runs fine on an iPhone 4s running iOS6.
Does CADisplayLink
function differently in iOS5.1
? Any possible workarounds/solutions?
I can't speak to the iOS 5.x v 6.x differences, but when I use
CADisplayLink
, I never hard code stuff like "move x pixels/points" every iteration, but rather I look at thetimestamp
(or more accurately, the delta between my initialtimestamp
and the currenttimestamp
) and calculate the location based upon how much time has elapsed, not by how many frames have passed. That way, frame rates don't affect the speed of the motion, but rather just the smoothness of it. (And the difference between 30 and 29 is likely to be indistinguishable.)To quote from the CADisplayLink Class Reference:
As a random example, here I'm animating a
UIBezierPath
using the number of seconds that have elapsed as a parameter.Or, alternatively, if you're dealing with a sequence of
UIImage
frames, you could calculate the frame number as follows:Or, better yet, to avoid risking losing a frame, go ahead and let this run at 60 fps and just determine if the frame needs updating and that way you'll reduce the risk of dropping a frame.
But frequently, frame numbers aren't needed at all. For example, to move a
UIView
in a circle, you might do something like:By the way, if you use Instruments to measure frame rate, it can seem slower than it really will be on a device. To matt's comment, for accurate frame rates, you should measure it programmatically on an actual device with a release build.
Basic Swift version of the other answers (minus the animation code)
Usage
Rob's answer is exactly right. You've no business worrying about the frame rate of CADisplayLink; in fact, you mustn't even expect that the timer will fire with anything like regularity. Your job is to divide up the desired animation in accordance with the desired time scale, and draw the frame you actually get each time the timer fires by adding up the accumulated timestamps.
Here is sample code from my book:
In that code, the
_frame
value runs between 0 (we are just starting the animation) and 1 (we have finished the animation), and in the middle I just do whatever this particular situation requires to draw that frame. To make the animation take longer or shorter, just multiply a scale factor when setting the_frame
ivar.Also note that you must never test in the Simulator, as the results are utterly meaningless. Only the device runs CADisplayLink properly.
(Example comes from here: http://www.apeth.com/iOSBook/ch17.html#_cifilter_transitions)