Bees & Bombs
Rebuilding a gif from Bees & Bombs
The core focus of C4 is to make building beautiful interactive experiences and interfaces. C4 is centered around Apple’s Core Animation (CA) framework, and makes programming interactivity, animations and media way faster and way easier than working directly with CA.
This tutorial highlights the basics of getting media on screen and, in some case, playing. It is broken into self-contained examples so that you can jump to any section without having to read through the entire thing.
I built all these examples using the C4 Single-view template, that comes from the installer.
All of the examples below can be pasted into the setup()
function directly in WorkSpace.swift
.
For example, here’s how you can run the circle example:
override func setup() {
let circle = Circle(center: canvas.center, radius: 50)
canvas.add(circle)
}
Shapes are all pretty straightforward: paths that you can style.
To create a square or a rectangle all you need to do is specify the frame into which it will be drawn:
let square = Rectangle(frame: Rect(0, 0, 100, 100))
square.center = canvas.center
canvas.add(square)
To create a circle you need to specify a center point and a radius:
let circle = Circle(center: canvas.center, radius: 50)
canvas.add(circle)
For anything that isn’t a circle, you specify the frame (just like a rectangle):
let ellipse = Ellipse(frame: Rect(0, 0, 200, 100))
ellipse.center = canvas.center
canvas.add(ellipse)
To create a line you need to specify a 2-element tuple (i.e. not an array) of Point
:
let points = (Point(), Point(100, 100))
let line = Line(points)
line.center = canvas.center
canvas.add(line)
Lines are special 2-point polygons whose end points can be updated
To create a triangle you need to specify a 3-element array of Point
:
let points = [Point(), Point(100, 100), Point(200, 0)]
let triangle = Triangle(points)
triangle.center = canvas.center
canvas.add(triangle)
Triangles are simply 3-point polygons that close between the last point and the first point.
To create a polygon you need to specify an n-length array of Point
:
let points = [Point(), Point(100, 100), Point(200, 0), Point(300, 100)]
let polygon = Polygon(points)
polygon.center = canvas.center
canvas.add(polygon)
Creating a regular polygon, whose sides are all equal in length, is similar to creating a circle:
let regularPolyon = RegularPolygon(center: canvas.center, radius: 50.0, sides: 6, phase: 0.0)
canvas.add(regularPolyon)
The phase refers to the inital angle from which to start drawing the shape.
Creating a star is also similar to creating a circle:
let star = Star(center: canvas.center, pointCount: 5, innerRadius: 25.0, outerRadius: 50.0)
canvas.add(star)
Creating an arc is similar to creating a circle, except you also need to add a start and end angle:
let arc = Arc(center: canvas.center, radius: 50, start: M_PI, end: 2 * M_PI)
canvas.add(arc)
Creating a wedge is identical to creating an arc, except that it draws the pie-piece all the way down to the center point of the shape:
let wedge = Wedge(center: canvas.center, radius: 50, start: 1.25 * M_PI, end: 1.75 * M_PI)
canvas.add(wedge)
You can create a text shape by specifying a string and a font:
let string = "C4"
let textShape = TextShape(text: string)!
textShape.center = canvas.center
canvas.add(textShape)
The
TextShape
initializer is optional because it depends on an optional font. So, you’ll need to handle it properly. Here, I know it will build properly so I force unwrap it with a!
.
C4 has its own color object, and there are a ton of default colors and 3 of our own colors that we’ve cooked into the framework.
The three default colors that we have in C4 are from the logo. There is C4Pink
, C4Purple
and C4Blue
. Here’s how to use them:
let r = Rectangle(frame: Rect(0, 0, 100, 100))
r.center = canvas.center
r.lineWidth = 8
r.strokeColor = C4Blue
r.fillColor = C4Pink
canvas.add(r)
canvas.backgroundColor = C4Purple
There are a bunch of default colors cooked into C4. Here’s how to use a couple of them:
let r = Rectangle(frame: Rect(0, 0, 100, 100))
r.center = canvas.center
r.lineWidth = 8
r.strokeColor = green
r.fillColor = blue
canvas.add(r)
canvas.backgroundColor = red
Check out
Color.swift
for all the default colors.
Creating a custom color is pretty straightforward, you need to specify the RGBA components:
canvas.backgroundColor = Color(red: 0.25, green: 0.5, blue: 0.75, alpha: 1.0)
You can create colors from images too! Do it:
let r = Rectangle(frame: Rect(0, 0, 96, 96))
r.center = canvas.center
r.fillColor = Color("pattern1")
r.lineWidth = 8.0
r.strokeColor = Color("pattern2")
canvas.add(r)
To add an image you simply specify the name of the file you want to use:
let image = Image("chop")!
image.center = canvas.center
canvas.add(image)
There are some nice filters that are already a part of C4. You can apply a filter to an image like this:
let image = Image("chop")
image?.center = canvas.center
canvas.add(image)
var dotScreen = DotScreen()
dotScreen.width = 10.0
image?.apply(dotScreen)
There are also image generators:
let image = Image()
image.frame = Rect(0, 0, 100, 100)
let checkerBoard = Checkerboard()
image.generate(checkerBoard)
image.center = canvas.center
canvas.add(image)
Notice that I didn’t use an optional here. This is because creating an empty image from a frame is always going to return an object. Unlike specifying a “fileName” that may or may not exist.
Like images, you can easily add movies to your apps by specifying the name:
let movie = Movie("halo.mp4")
movie?.center = canvas.center
canvas.add(movie)
movie?.play()
Unlike images, you need to specify the file extension of the movie.
Even though you specify the file name (incl. extension) creating an audio players is a little different than a movie. You need to create a variable outside the scope of the method you’re using to set it up:
let player = AudioPlayer("C4Loop.aif")
override func setup() {
player?.loops = true
player?.play()
}
When a movie is added to the canvas it gets a strong reference because it’s a subview of the canvas. Since an audio player is not a visible object to add to a subview, you need to store a reference to it as a variable.
Compared to UIKit, adding a gesture is a cinch. In C4, every visible object is ready to have gestures attached to it, even the main canvas.
You can add a tap gesture to any object like so:
let square = Rectangle(frame: Rect(0, 0, 100, 100))
square.center = canvas.center
canvas.add(square)
square.addTapGestureRecognizer { locations, center, state in
let randomColor = Color(red: random01(), green: random01(), blue: random01(), alpha: 1.0)
self.canvas.backgroundColor = randomColor
}
You can add a pan gesture to any object. In this example, panning (i.e. dragging) on the canvas will update the position of the square:
let square = Rectangle(frame: Rect(0, 0, 100, 100))
square.center = canvas.center
canvas.add(square)
canvas.addPanGestureRecognizer { locations, center, translation, velocity, state in
square.center = location
}
Almost every single object in C4 is animatable in one way or another. Visible objects have the most animatable properties, but others like AudioPlayers
can also animate things like their pan properties.
In general, a good rule of thumb is:
If an object has a property, it’s probably animatable.
To animate something you create a ViewAnimation
and put some code to change inside that object’s block. For example, the background
property of any object (including the canvas) is animatable:
ViewAnimation(duration: 1.0) {
self.canvas.backgroundColor = C4Blue
}.animate()
The previous block will immediately animate the background color to C4Blue
, taking 1 second to do so.
You can also create the animation as an object, change its properties and animate it later, like so:
let anim = ViewAnimation(duration: 1.0) {
self.canvas.backgroundColor = C4Blue
}
anim.curve = .EaseOut
anim.repeats = true
wait(1.0) {
anim.animate()
}
If you have a bunch of animations that you want to start at the exact same time, you can do that by creating a ViewAnimationGroup
:
let square = Rectangle(frame: Rect(0, 0, 100, 100))
square.center = canvas.center
canvas.add(square)
let circle = Circle(center: canvas.center, radius: 50)
canvas.add(circle)
let squareAnim = ViewAnimation(duration: 1.0) {
square.transform = Transform.makeRotation(M_PI)
}
let circleAnim = ViewAnimation(duration: 1.0) {
let randomColor = Color(red: random01(), green: random01(), blue: random01(), alpha: 1.0)
circle.fillColor = randomColor
}
let group = ViewAnimationGroup(animations: [squareAnim, circleAnim])
wait(1.0) {
group.animate()
}
To execute a bunch of animations one after another you can use a ViewAnimationSequence
:
let square = Rectangle(frame: Rect(0, 0, 100, 100))
square.center = canvas.center
canvas.add(square)
let circle = Circle(center: canvas.center, radius: 50)
canvas.add(circle)
let squareAnim = ViewAnimation(duration: 1.0) {
square.transform = Transform.makeRotation(M_PI)
}
let circleAnim = ViewAnimation(duration: 1.0) {
let randomColor = Color(red: random01(), green: random01(), blue: random01(), alpha: 1.0)
circle.fillColor = randomColor
}
let sequence = ViewAnimationSequence(animations: [squareAnim, circleAnim])
wait(1.0) {
sequence.animate()
}
Shapes. Images. Movies. Audio. Gestures. Animations. These are the basics you’ll need to build any of the amazing iOS experiences you can imagine.
Grab of a copy of the the code from this tutorial: Basics Tutorial Code
Seriously. Complex interfaces and UI elements are all really just made up of these fundamental building blocks.