Graphics Reference
In-Depth Information
▪
We can adjust the
run loop mode
of our animation timer so that it won't be delayed
by other events.
CADisplayLink
CADisplayLink
is an
NSTimer
-like class provided by Core Animation that always fires
immediately prior to the screen being redrawn. Its interface is very similar to
NSTimer
, so it
is essentially a drop-in replacement, but instead of a
timeInterval
specified in seconds,
the
CADisplayLink
has an integer
frameInterval
property that specifies how many
frames to skip each time it fires. By default, this has a value of 1, meaning that it should fire
every frame. But if your animation code takes too long to reliably execute within a sixtieth
of a second, you can specify a
frameInterval
of 2, meaning that your animation will
update every other frame (30 frames per second) or 3, resulting in 20 frames per second,
and so on.
Using a
CADisplayLink
instead of an
NSTimer
will produce a smoother animation by
ensuring that the frame rate is as consistent as possible. But even
CADisplayLink
cannot
guarantee
that every frame will happen on schedule. A stray task or an event outside of
your control (such as a resource-hungry background application) might still cause your
animation to occasionally skip frames. When using an
NSTimer
, the timer will simply fire
whenever it gets a chance, but
CADisplayLink
works differently: If it misses a scheduled
frame, it will skip it altogether and update at the next scheduled frame time.
Measuring Frame Duration
Regardless of whether we use an
NSTimer
or
CADisplayLink
, we still need to handle the
scenario where a frame takes longer to calculate than the expected time of one-sixtieth of a
second. Because we cannot know the actual frame duration in advance, we have to measure
it as it happens. We can do this by recording the time at the start of each frame using the
CACurrentMediaTime()
function, and then comparing it to the time recorded for the
previous frame.
By comparing these times, we get an accurate frame duration measurement that we can use
in place of the hard-coded one-sixtieth value for our timing calculations. Let's update our
example with these improvements (see Listing 11.2).
Listing 11.2
Measuring Frame Duration for Smoother Animation
@interface
ViewController ()
@property
(
nonatomic
,
weak
)
IBOutlet
UIView
*containerView;
@property
(
nonatomic
,
strong
)
UIImageView
*ballView;
@property
(
nonatomic
,
strong
)
CADisplayLink
*timer;
@property
(
nonatomic
,
assign
)
CFTimeInterval
duration;
@property
(
nonatomic
,
assign
)
CFTimeInterval
timeOffset;