I\'m using CADisplayLink
in my iPhone app.
Here is the relevant code:
SMPTELink = [CADisplayLink displayLinkWithTarget:self selector:@selec
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 the timestamp
(or more accurately, the delta between my initial timestamp
and the current timestamp
) 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:
Once the display link is associated with a run loop, the selector on the target is called when the screen’s contents need to be updated. The target can read the display link’s timestamp property to retrieve the time that the previous frame was displayed. For example, an application that displays movies might use the timestamp to calculate which video frame will be displayed next. An application that performs its own animations might use the timestamp to determine where and how displayed objects appear in the upcoming frame. The duration property provides the amount of time between frames. You can use this value in your application to calculate the frame rate of the display, the approximate time that the next frame will be displayed, and to adjust the drawing behavior so that the next frame is prepared in time to be displayed.
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:
@property (nonatomic) CFTimeInterval firstTimestamp;
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
NSInteger frameNumber = (NSInteger)(elapsed * kFramesPerSecond) % kMaxNumberOfFrames;
// now do whatever you want with this frame number
}
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.
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
NSInteger frameNumber = (NSInteger)(elapsed * kFramesPerSecond) % kMaxNumberOfFrames;
if (frameNumber != self.lastFrame)
{
// do whatever you want with this frame number
...
// now update the "lastFrame" number property
self.lastFrame = frameNumber;
}
}
But frequently, frame numbers aren't needed at all. For example, to move a UIView
in a circle, you might do something like:
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
if (!self.firstTimestamp)
self.firstTimestamp = displayLink.timestamp;
CFTimeInterval elapsed = (displayLink.timestamp - self.firstTimestamp);
self.animatedView.center = [self centerAtElapsed:elapsed];
}
- (CGPoint)centerAtElapsed:(CFTimeInterval)elapsed
{
CGFloat radius = self.view.bounds.size.width / 2.0;
return CGPointMake(radius + sin(elapsed) * radius,
radius + cos(elapsed) * radius);
}
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.