CALayer Tutorial for iOS: Getting Started

In this article, you’ll learn about CALayer and how it works. You’ll use CALayer for cool effects like shapes, gradients and particle systems. By Ron Kliffer.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

Locking Scrolling Directions

Add the following code to scrollingSwitchChanged(_:):

switch (horizontalScrollingSwitch.isOn, verticalScrollingSwitch.isOn) {
case (true, true):
  scrollingViewLayer.scrollMode = .both
case (true, false):
  scrollingViewLayer.scrollMode = .horizontally
case (false, true):
  scrollingViewLayer.scrollMode = .vertically
default:
  scrollingViewLayer.scrollMode = .none
}

Here, you add a simple switch block. It determines the scrolling direction according to the values of the switches in the user interface.

Now build and run. Go back to the app, flip the switches and see how CAScrollLayer behaves.

Here are some rules of thumb for when to use — or not to use — CAScrollLayer:

  • If you want something lightweight and you only need to scroll programmatically, consider using CAScrollLayer.
  • When you want the user to be able to scroll, you’re better off with UIScrollView. To learn more, check out our Scroll View School video tutorial series.
  • If you’re scrolling a very large image, consider using CATiledLayer.

Rendering Text With CATextLayer

CATextLayer provides simple but fast rendering of plain text or attributed strings. Unlike UILabel, a CATextLayer cannot have an assigned UIFont, only a CTFont or CGFont.

Open CATextLayerViewController.swift and add the following to the end of setUpTextLayer():

// 1
textLayer.font = helveticaFont
textLayer.fontSize = Constants.baseFontSize

// 2
textLayer.foregroundColor = UIColor.darkGray.cgColor
textLayer.isWrapped = true
textLayer.alignmentMode = .left
textLayer.truncationMode = .end

// 3
textLayer.contentsScale = UIScreen.main.scale

Here’s what you’re doing:

  1. You set the font of the text layer. Notice that this is a CTFont and not a UIFont. You create these using CTFontCreateWithName(_:_:_:). CATextLayer lets you set the font size directly on the layer, as you do here.
  2. Next, you set the text color, wrapping, alignment and truncation modes. All of these are also available on a regular UILabel or UITextView.
  3. Finally, you set the layer’s contentsScale to match the screen’s scale. All layer classes, not just CATextLayer, render at a scale factor of 1 by default. Attaching a layer to a view automatically sets its contentsScale to the appropriate scale factor for the current screen. For any layer you create manually, however, you must set contentsScale explicitly. Otherwise, its scale factor will be 1, causing it to appear pixelated on Retina displays.

Build and run, then select CATextLayer from the menu. Layer Player has controls to change many of CATextLayer‘s properties. Play around with them to see what they do:

Styling your Text

Next, you’ll give Layer Player the ability to play media files.

Playing Media With AVPlayerLayer

AVPlayerLayer adds a sweet layer of goodness to AVFoundation. It holds an AVPlayer to play media files of type AVPlayerItem.

Setting up Media Playback

Open AVPlayerLayerViewController.swift and add the following code to setUpPlayerLayer():

// 1
playerLayer.frame = viewForPlayerLayer.bounds

// 2
let url = Bundle.main.url(forResource: "colorfulStreak", withExtension: "m4v")!
let item = AVPlayerItem(asset: AVAsset(url: url))
let player = AVPlayer(playerItem: item)

// 3
player.actionAtItemEnd = .none

// 4
player.volume = 1.0
player.rate = 1.0


playerLayer.player = player

In this code, you set up the player. Here’s how:

  1. First, you set the layer’s frame.
  2. Next, you create a player with an AVPlayerItem.
  3. You tell the player to do nothing when it finishes playing. Additional options include pausing or advancing to the next asset, if applicable.
  4. Finally, you set the volume and rate of the player.

Now that you can play media files, you’re going to give your users the ability to change the speed at which they play.

Changing Playback Speeds

rate sets the video playing speed. 0 means pause and 1 means the video plays at regular speed.

However, setting rate also instructs playback to commence at that rate. In other words, calling pause() and setting rate to 0 does the same thing, as does calling play() and setting rate to 1!

So what about fast forward, slow motion or playing in reverse? Well, AVPlayer has you covered!

When you set rate to anything higher than 1, playback commences at that number times the regular speed. For instance, setting rate to 2 means double speed. And setting rate to a negative number results in playback at that number times regular speed in reverse.

Before playback occurs at any rate other than forward at regular speed, however, you should check the appropriate variable in AVPlayerItem to verify that it can be played back at that rate:

  • canPlayFastForward for any number higher than 1.
  • canPlaySlowForward for any number between 0 and up to, but not including, 1.
  • canPlayReverse for -1.
  • canPlaySlowReverse for any number between -1 and up to, but not including, 0.
  • canPlayFastReverse for any number lower than -1.

Most videos can play at various forward speeds, but it’s less common that they can play in reverse.

Updating the User Interface

Now, back to the code. When you tap the play button, it should toggle the controls to play AVAsset and set the button’s title.

Add the following to playButtonTapped(_:):

if player?.rate == 0 {
  player?.rate = rate
  updatePlayButtonTitle(isPlaying: true)
} else {
  player?.pause()
  updatePlayButtonTitle(isPlaying: false)
}

Here, you toggle the player’s state and update the play button’s title, depending on the initial rate.

Finally, you’ll add code to move the playback cursor back to the beginning when the player has reached the end of the media file.

Resetting the Playback Cursor

In viewDidLoad(), you can see that AVPlayerItemDidPlayToEndTimeNotification has an observer. This notification is called when an AVPlayerItem has reached the end.

Add the following code to playerDidReachEndNotificationHandler(_:):

// 1
guard let playerItem = notification.object as? AVPlayerItem else { return }

// 2
playerItem.seek(to: .zero, completionHandler: nil)
    
// 3
if player?.actionAtItemEnd == .pause {
  player?.pause()
  updatePlayButtonTitle(isPlaying: false)
}

In this code:

  1. You verify that the notification object is an AVPlayerItem.
  2. You use seek(to:completionHandler:) to send the player to a desired position. In this case, you send the player to CMTime.zero — which is the beginning.
  3. You pause the player if its actionAtItemEnd is set to .pause, and you set the button text accordingly.

Build and run and select AVPlayerLayer from the menu. Change the values on the controls to see how each one changes the layer’s behavior.

Media player with options to change the playback speed

For your next step, you’ll see how easy it is to make cool gradients with CALayer.