Core Animation Swift Tutorial – Animatable Properties

This tutorial series requires a basic understanding of UIView hierarchy. If you are new to iOS development, you might want to begin with Developing iOS Apps Using Swift first.

When you first hear about Core Animation, you might think it is all about animation. However, animation is only a part of this framework. Core Animation is an infrastructure for compositing and manipulating visual content onscreen. It uses the GPU to accelerate the rendering of on-screen objects. It divides the content onscreen into individual objects called layers, and arranges them in a tree hierarchy (known as the layer tree). You are familiar with the UIView tree hierarchy, which is built upon the layer tree. In other words, Core Animation accounts for everything that you see onscreen.

In the first part of this tutorial series, you are going to learn the basics of CALayer, how to use its properties to create neat visual effects easily, and at the end of this tutorial, you will learn the most important subclass of CALayer – CAShapeLayer.

What Is CALayer?

CALayers are rectangular objects that can contain visual content. They are stored into a tree hierarchy, and each manages the position of its children sublayers.

Sound familiar? You may say, “It’s like UIView!”

That’s true, but it’s not just a coincidence. Every UIView has a layer property known as the backing layer, which is an instance of CALayer. UIView just ensures the corresponding back layers are correctly connected in the layer tree when subviews are added or removed from the view. It is the backing layer that is responsible for display and animation of the view. The only major feature that the backing layer does not handle is user interaction.

For the purposes of going through this tutorial, I recommend creating an empty single-view iPhone application.

Exploring CALayer

Creating a CALayer instance is very straightforward.

let redLayer = CALayer()

We can set its frame, backgroundColor, and add it to a superlayer just like we do with UIView.

redLayer.frame = CGRect(x: 50, y: 50, width: 100, height: 100)
redLayer.backgroundColor = UIColor.redColor().CGColor
layer.addSublayer(redLayer)

Add this code to a function called setup in the file ViewController.swift, and call the method from viewDidLoad. You should have something like this:

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setup()
    }
    func setup() {
        let redLayer = CALayer()
        
        redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
        redLayer.backgroundColor = UIColor.redColor().CGColor
        self.view.layer.addSublayer(redLayer)
    }
}

Notice that the backgroundColor property of CALayer is a CGColor instead of UIColor.

You can also set an image as the content via the contents property. contents is also known as backing image.

We’ll use an image of a butterfly:

Note that you’ll have to drag your image named ButterflySmall.jpg in to your Xcode project hierarchy in order for the UIImage() command to find the picture.

func setup() {
    let redLayer = CALayer()
    
    redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
    redLayer.backgroundColor = UIColor.redColor().CGColor
    self.view.layer.addSublayer(redLayer)
    
    
    let imageLayer = CALayer()
    let image = UIImage(named: "ButterflySmall.jpg")!
    imageLayer.contents = image.CGImage
    
    imageLayer.frame = CGRect(x: 0, y: 100, width: image.size.width, height: image.size.height)
    imageLayer.contentsGravity = kCAGravityResizeAspect
    imageLayer.contentsScale = UIScreen.mainScreen().scale
    
    self.view.layer.addSublayer(imageLayer)
}

contentsGravity is a constant that specifies how the layer’s contents are positioned or scaled within its bounds.

contentsScale defines a ratio mapping between the size of the layer (measured in points) and the size of the bitmap used to present the layer’s content (known as backing image, measured in pixels). The default value is 1.0. Normally we set the value as the scale of the image, as shown above. However, when working with image that are generated programmatically, or image that missing @2x/@3x suffix, you will remember to manually set the layer’s contentsScale to match the screen scale. Otherwise, you will get a pixelated image on your device.

Corners and Border

CALayer has a property called cornerRadius, which applies rounded corners to the layer’s background and border. When the masksToBounds property is set to true, the layer’s backing image and sublayers are clipped to this curve.

On our redLayer, let’s apply some rounded corners, and make the border visible.

func setup() {
    let redLayer = CALayer()
    
    redLayer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
    redLayer.backgroundColor = UIColor.redColor().CGColor
    
    // Round corners
    redLayer.cornerRadius = 25
    
    // Set border
    redLayer.borderColor = UIColor.blackColor().CGColor
    redLayer.borderWidth = 10
    ...

borderWidth, borderColor defines the width and color of a layer’s border.

Drop Shadow

There are four properties that you could configure the drop shadow for a layer, shadowOpacity, shadowColor, shadowOffset and shadowRadius . shadowRadius controls the blurriness of the shadow. A larger value could create a softer shadow that looks more natural.

Let’s add a shadow to our redLayer as well.

redLayer.shadowColor = UIColor.blackColor().CGColor
redLayer.shadowOpacity = 0.8
redLayer.shadowOffset = CGSizeMake(2, 2)
redLayer.shadowRadius = 3

Unlike the layer border, the layer’s shadow derives from the exact shape of its contents, not just the bounds and cornerRadius. However, if you know what the shape of your shadow would be in advance, you could specify it via shadowPath (an instance of CGPath). You could improve performance by doing this.

Animating Layers

Now that we’ve covered a few of the types of properties that are present in Core Animation, let’s quickly walk through creating some actual animations.

// Create a blank animation using the keyPath "cornerRadius", the property we want to animate
let animation = CABasicAnimation(keyPath: "cornerRadius")

// Set the starting value
animation.fromValue = redLayer.cornerRadius

// Set the completion value
animation.toValue = 0

// How may times should the animation repeat?
animation.repeatCount = 1000

// Finally, add the animation to the layer
redLayer.addAnimation(animation, forKey: "cornerRadius")

Here we create a new CABasicAnimation for the cornerRadius property. Run your app and take a look, cool right?

You could just as easily make this animation apply to any other animatable property of CALayer. Try swapping the “cornerRadius” keyPath value in the CABasicAnimation() constructor with the value “borderWidth”. What happens? What about a value of “shadowRadius”?

From this tutorial you can see how the basics of Core Animation work. In the next part we’ll move on to learn about more animatable properties, and how to work with masks to do really nifty effects.

Full code for this tutorial available here, on Github.

Want to get notified when the next part is out? Be sure to subscribe to our newsletter here.


Sign up now and get a set of FREE video tutorials on writing iOS apps coming soon.



Subscribe via RSS