In this episode we take our missile animation from last time and update it to use CAAnimations. Using these animations we can add a wiggle, along with a small oscillating rotation to give a little life to the missile. Then we utilize CAReplicatorLayer to have them fan out and fire in slightly different directions.
Episode Links Source Code CAReplicatorLayer Class Reference CABasicAnimation Class Reference Creating our Rocket Layer let rocketImage = UIImage(named: "missile")! let rocket = CALayer() let size = rocketImage.size rocket.contents = rocketImage.CGImage rocket.frame = CGRect(x: -size.width, y: 100, width: size.width, height: size.height) We're starting it off-screen so we can animate it in smoothly. Animating it across the screen To do this we'll use a transform animation and translate the X position to a spot far off the right side of the screen. let travel = CABasicAnimation(keyPath: "transform.translation.x") travel.duration = 1.5 travel.fromValue = 0 travel.toValue = view.frame.size.width + 400 travel.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) Here we use ease in as the timing function because we don't want it to slow down at the end. Adding a little wiggle To make the rocket wiggle in the Y direction, we'll do another translation, but this time we'll have it automatically reverse direction, using a smooth ease-in/ease-out curve. let wiggle = CABasicAnimation(keyPath: "transform.translation.y") wiggle.autoreverses = true wiggle.fromValue = 0 wiggle.toValue = -94 wiggle.duration = 0.35 wiggle.repeatCount = Float.infinity wiggle.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) Rotating synchronized with the wiggle animating To have our rocket seem to point in the direction of the wiggle we can rotate the layer slightly. We'll copy the duration, & repeat settings from wiggle and animate back & forth from a small angle. We'll also have to make sure that we start our layer with the right angle so the animation is consistent. let angle = CGFloat(M_PI / 16.0) rocket.transform = CATransform3DMakeRotation(-angle, 0, 0, 1.0) let wiggleRotation = CABasicAnimation(keyPath: "transform.rotation") wiggleRotation.beginTime = 0.08 wiggleRotation.duration = wiggle.duration wiggleRotation.fromValue = -angle wiggleRotation.toValue = angle wiggleRotation.autoreverses = true wiggleRotation.repeatCount = Float.infinity The beginTime is set to delay this animation slightly so it matches up visually with the wiggle animation. This number should be tweaked until it looks right. Replicating the rocket Finally, we can make multiple copies of the rocket layer & animation by using CAReplicatorLayer. We'll set a small delay on each instance so they don't all come in at once, and we'll add a rotation so they fan out and travel in different directions. let replicator = CAReplicatorLayer() replicator.instanceCount = 10 replicator.instanceDelay = 0.1 replicator.instanceTransform = CATransform3DMakeTranslation(0, 90, 0) replicator.instanceTransform = CATransform3DRotate(replicator.instanceTransform, CGFloat(M_PI / 80.0), 0, 0, 1.0) Finalizing the animation Now we just need to add these animations to a group, and add our replicator to our view's layer tree. let anim = CAAnimationGroup() anim.animations = [ travel, wiggle, wiggleRotation ] anim.duration = travel.duration anim.fillMode = kCAFillModeForwards anim.removedOnCompletion = false view.layer.addSublayer(replicator) rocket.addAnimation(anim, forKey: "group") replicator.addSublayer(rocket)