iOS Recipes - Matt Drance [57]
By using your own graphics context, you’re able to draw the objects you need and easily build a series of images that can be animated in sequence.
Discussion
Could you get the same result some other way? Sure—you could use an NSTimer, changing the contents of the view each time the timer fires, but because the timer is triggered by the run loop, the potential for delays is greater than with UIView animation, which runs in its own thread.
Recipe 29 Construct a Simple Emitter
Problem
A particle emitter has various uses in a graphical application—for example, as a background visual or as a dynamic component of a larger view. It would be nice if a particle emitter were available on iOS, but currently this feature is available only on Mac OS X. If you wanted to build one yourself, you would normally need to descend into OpenGL. Is it possible to create a simple emitter using only Core Animation?
Solution
We can create a simple emitter using Core Animation, but to get reasonable performance, we need to consider how best to do it. An emitter is usually required to handle a large number of particles, each of them being a distinct graphic element. Though we could use Core Animation to create a layer for each particle, this would be very expensive in terms of both memory and GPU time. The processor time needed to instantiate a large number of layers could also be a limiting factor.
An alternate solution then would be for us to use a combination of the CAReplicatorLayer class and CABasicAnimation. The CAReplicatorLayer class is a little-used subclass of CALayer that uses the GPU to create exact duplicates of the contents of the original layer, while varying some of the properties by small incremental changes as each copy is rendered. Only the sublayers of the replicator layer are duplicated, so we must always include a source layer, or layers, that contains the visual elements.
For example, using a CAReplicatorLayer, we could create a line of images, each an exact copy of the original, by setting the contents of the main sublayer to the image; setting the instanceTransform to, say, 10 points of X translation; and setting the instanceCount to 10. This would create a line of 10 duplicate images all 10 points apart—useful, but not quite what we want (see Figure 33, Four separate instances of the simple emitter ).
Figure 33. Four separate instances of the simple emitter
* * *
To create the emitter effect, we need to animate the position of the image sublayer. As the sublayer moves, each of its duplicates also moves so that we now have a moving line of images. By adding another component, instanceDelay, we can really augment this effect because each duplicate element is added only after that specified delay. A fraction of a second is enough to create the kind of effect we want.
We create a new class, PRPSimpleEmitterLayer, which is a subclass of CAReplicatorLayer, to add the coordinated animation to the base class. In the init method, we add our sublayer and set its contents to the default image, in this case a small bright spark.
SimpleEmitter/SimpleEmitter/PRPSimpleEmitterLayer.m
- (id)init {
self = [super init];
if (self) {
self.count = 1;
self.instanceColor = [UIColor whiteColor].CGColor;
imageLayer = [CALayer layer];
self.imageLayer.contents =
(id)[UIImage imageNamed:@"tspark.png"].CGImage;
[self addSublayer:self.imageLayer];
}
return self;
}
The start method configures the properties for the imageLayer and the replicator layer, based on any modified properties from the view controller. We calculate the instanceDelay from the cycleTime and the count, spreading out the duplicated elements evenly for the duration of the animation.
The incremental property, rotator, is the angle of rotation added to