Making It Snow

The snow effect was vital to making CoCo Love work, so after doing some research I found out that it’s quite simple to implement using 2 classes; CAEmitterLayer and CAEmitterCell. The CAEmitterLayer class provides a “particle emitter system for Core Animation”. The particles themselves are defined by CAEmitterCell and both classes are highly customizable. First let’s look at the cell.

lazy var snowCell: CAEmitterCell = {

let snowCell = CAEmitterCell()

snowCell.contents = UIImage(named: “snowflake”)?.CGImage

snowCell.scale = 0.06

snowCell.scaleRange = 0.03

snowCell.emissionRange = CGFloat(M_PI_2)

snowCell.lifetime = 20.0

snowCell.birthRate = 15

snowCell.velocity = -30

snowCell.velocityRange = -20

snowCell.yAcceleration = 10

snowCell.spin = -0.5

snowCell.spinRange = 1.0



return snowCell

}()

There’s quite a lot of attributes here, most of which are self explanatory. First you set the contents to an image. In this case a snowflake. The original snowflake image is too large, so you can adjust the size with the scale property. Of course things would look too uniform if every snowflake looked and behaved exactly the same, so luckily most of the attributes have a companion range property that specifies the amount the attribute can vary over its lifetime. You can see these being set above with scaleRange, emissionRange, velocityRange and spinRange.

The emissionRange property defines the angle of a cone in which cells are emitted from the layer. If it’s left at the default value of zero, then the snowflakes would drop straight down without any horizontal variance.

The lifetime attribute is set to a value high enough in seconds so that the cells don’t disappear while on screen. Finally, birthrate is the spawn rate for the particles defined by cells per second.

CAEmitterCell does nothing on its own so next we declare and initialize the CAEmitterLayer class.

lazy var snowLayer: CAEmitterLayer = {

let snowLayer = CAEmitterLayer()

snowLayer.emitterPosition =

CGPointMake(self.view.bounds.width/2.0, 0)

snowLayer.emitterSize =

CGSizeMake(self.view.bounds.width, 0)

snowLayer.emitterShape = kCAEmitterLayerLine

snowLayer.beginTime = CACurrentMediaTime()

snowLayer.emitterCells = [self.snowCell]



return snowLayer

}()

Pretty straight forward. We create the layer, centre it at the top of the view and set it to the full width of the view controller. There are different shapes the layer can be set as to emit particles using emitterShape, but in this case we just want a simple line for snowflakes to fall from.

The beginTime attribute might not seem like it’s needed at first but the particle system won’t work quite as expected if you don’t set it to CACurrentMediaTime(). I’ll leave it up to readers to experiment with and find out why. ☺

Finally before adding the layer to the view we add the snowCell defined earlier to the emitterCells property. Then all we need is the following to make it snow!

self.view.layer.addSublayer(self.snowLayer)

One thing to mention is that the emitterCells property is an array, so you can add multiple cells all with their own customizations. It’s worth experimenting here to try adding multiple cells with different attributes to get some fun effects.

You may have noticed the lazy modifier before the snowCell and snowLayer declarations above. This is a great convenience feature in Swift which initializes the property only when needed. This is simpler to implement than the objective-c equivalent which is to use dispatch_once with a static dispatch_once_t predicate.

Using the lazy modifier here is easy to use and slightly more efficient than without because it initializes the variable only when needed (i.e. when the user clicks the snowflake button 5 times). I personally feel like it makes for cleaner code as well, because all the cell and layer attributes can be set at the top of the view controller along with other property declarations instead of deeper down in the view controller in some event triggered function. This is completely a matter of preference though and I’d be interested to hear what other people think.

It’s also worth noting that you can call self when initializing lazy properties. Normally this isn’t the case because the view controller hasn’t finished initializing yet while properties are being initialized. With lazy properties however, everything is already set and it’s safe to call self in the block. You can read more about initializations from Apple’s Swift docs.

Wrap Up

Completing CoCo Love is not hard from here. All that’s needed are some not-so-fancy animations and AVAudioPlayer to play the music clips. I did try to make the code as clean and compact as possible, so helper functions are used for all repeated tasks. Here’s all that needs implementing beyond a couple of @IBAction outlets for the buttons:

func flashBackground()

func startPlayer(player: AVAudioPlayer)

func stopPlayer(player:AVAudioPlayer)

func animateScale(view: UIView, duration: NSTimeInterval)

func removeScale(view: UIView)

func makeItSnow() // Add the snowLayer

And there you have it. Everything behind making the hit app CoCo Love!