Bees & Bombs
Rebuilding a gif from Bees & Bombs
Your mission, should you choose to accept it, is to build the Skype loading animation using C4.
This is what the animation looks like:
This is a great animation to write a tutorial on because although it looks great, it’s fairly simple when it’s broken down. The circles in the animation are identical, except for their start time and duration.
A circle begins animating, then after a short delay another circle begins to animate, until all four circles have started animating. To have all the circles converge near the end of their rotation, their animation duration is less their delay.
Now, I’ll walk you through the steps to recreate this animation.
If you haven’t already done so, install C4 by following the instructions on our install tutorial. The easiest is to use the first option.
Create a new C4 Project
Though there are 4 circles in the Skype loading animation, we’re going to use an additional 5 invisible views to create the effect we want to achiever.
Let’s get to it.
In your project’s WorkSpace
add two arrays and a View
as class variables. Override setup()
and set the canvas.backgroundColor
to a nice blue and call createViewsCircles()
.
1
2
3
4
5
6
7
8
9
10
11
class WorkSpace: CanvasController {
var views = [View]()
var circles = [Circle]()
var container: View!
override func setup() {
canvas.backgroundColor = Color(red: 64, green: 177, blue: 239, alpha: 1.0)
createViewsCircles()
}
}
Create a function called createViewsCircles()
. In this function, you’re going to initialize a container, 4 views and 4 circles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func createViewsCircles() {
container = View(frame: Rect(0,0,1,1))
for _ in 1...4 {
let v = View(frame: Rect(0,0,10,10))
views.append(v)
let c = Circle(center: v.center, radius: v.width/2.0)
c.lineWidth = 10.0
c.strokeColor = white
c.fillColor = white
circles.append(c)
v.add(c)
v.anchorPoint = Point(0.5,7.5)
v.center = container.center
container.add(v)
}
canvas.add(container)
container.center = canvas.center
}
This is what you should see:
It looks like a single dot, but dots can be deceiving.
This method does the following:
Changing the anchor point of a view offsets what it “believes” is its center point.
There’s a particular motion to the Skype loader. What we found was this:
From this process we deduced a couple of things:
.EaseInOut
effect throughout its entire motionSo,…
It is the combination of an
.EaseInOut
motion of the view’s rotation within the motion of its superview container that creates the proper timing and easing for the entire animation
There are two major steps in this section: animate the views, and animate the circles. We’ll start with the views.
Add the following class variables and a constant for the duration.
1
2
let duration = 3.0
var viewAnimationGroup: ViewAnimationGroup!
Then, add the following function to your WorkSpace
:
1
2
3
4
5
6
7
8
9
10
11
12
13
func createAnimations() {
var vanims = [ViewAnimation]()
for i in 0..<views.count {
let v = views[i]
let offset = Double(i) * 0.1 + 0.05
let va = ViewAnimation(duration: duration/4.0 + 0.3) {
v.rotation += M_PI
}
va.delay = offset
vanims.append(va)
}
viewAnimationGroup = ViewAnimationGroup(animations: vanims)
}
Each view starts its rotation with a slight offset.
Then, call that method in setup()
…
1
2
3
4
5
override func setup() {
canvas.backgroundColor = Color(red: 64, green: 177, blue: 239, alpha: 1.0)
createViewsCircles()
createAnimations()
}
For this example, we’re going to trigger the animations with a repeating timer. Add the following variable to your WorkSpace
:
1
var animationTimer: Timer!
Then, at the end of setup()
, right after creating the animations, add the following:
1
2
3
4
5
animationTimer = Timer(interval: duration/4.0) {
self.viewAnimationGroup.animate()
}
animationTimer.start()
animationTimer.fire()
Press play, and you should see this:
The simple motion of the views.
Now, let’s modify the createAnimations()
function to generate animations for the shrinking and growing of each circle. Add another ViewAnimationGroup
to the WorkSpace
:
1
var circleAnimationGroup: ViewAnimationGroup!
Then, modify createAnimations()
to look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
func createAnimations() {
var canims = [ViewAnimation]()
var vanims = [ViewAnimation]()
for i in 0..<views.count {
let v = views[i]
let offset = Double(i) * 0.1 + 0.05
let va = ViewAnimation(duration: duration/4.0 + 0.3) {
v.rotation += M_PI
}
va.delay = offset
vanims.append(va)
let c = circles[i]
let ca = ViewAnimation(duration: duration/8.0 + 0.15) {
c.lineWidth = 0.0
}
ca.delay = offset
ca.autoreverses = true
ca.addCompletionObserver {
ShapeLayer.disableActions = true
c.lineWidth = 10.0
ShapeLayer.disableActions = false
}
canims.append(ca)
}
viewAnimationGroup = ViewAnimationGroup(animations: vanims)
circleAnimationGroup = ViewAnimationGroup(animations: canims)
}
Next, trigger the circle animations from the timer:
1
2
3
4
animationTimer = Timer(interval: duration/4.0) {
self.viewAnimationGroup.animate()
self.circleAnimationGroup.animate()
}
Press play, and you should see this:
Nice. Almost there…
The container also needs to rotate, so the last step is to create an animation to handle that. In setup()
add the last bit of code.
1
2
3
4
5
6
let containerAnim = ViewAnimation(duration: duration) {
self.container.rotation += 2*M_PI
}
containerAnim.curve = .Linear
containerAnim.repeats = true
containerAnim.animate()
Run your app now and you should see something that looks like this:
That was an easy one…
Actually, it took us a long time to decipher the motion of the circles and figure out how to re-construct the animation usin layers and layers of views.
###Code You can grab a copy of the code for this project from HERE