Chapters

Hide chapters

iOS Animations by Tutorials

Sixth Edition · iOS 13 · Swift 5.1 · Xcode 11

Section IV: Layer Animations

Section 4: 9 chapters
Show chapters Hide chapters

16. Gradient Animations
Written by Marin Todorov

A lot of the look and feel of iOS comes from very subtle animations in the UI.

While it is no longer a part of iOS, one of the nicest was a simple little animation: the “slide to unlock” label on the lock screen.

In this chapter you’ll learn how to mimic this effect with a moving gradient and how to animate the colors and layout of those gradients:

You’ll animate the gradient for a “Slide to reveal” label and then reveal a cool mystery effect when the user swipes over the label. You’ll have to work through this chapter, however, to see what this cool effect is!

As an extra bonus, you’ll learn how to create a layer mask out of a piece of text and use it to mask a gradient.

Drawing your first gradient

Open the starter project for this chapter and select Main.storyboard to see how the UI looks at present:

There’s a static label on top that mimics the iPhone clock on the lock screen and another view near the bottom.

The bottom view is an instance of AnimatedMaskLabel that’s included with the starter project. You’ll work with this class throughout this chapter to add gradient animations.

Build and run your project; you’ll see just the faux clock appear at the top of the screen:

You’ll first draw the base gradient of AnimatedMaskLabel. Add the following code to AnimatedMaskLabel.swift inside the gradientLayer property code after the comment shown below:

// Configure the gradient here
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)

This defines the orientation of the gradient and its start and end points.

Now add the following code to define the colors that build up the gradient after the code you just added:

let colors = [
  UIColor.black.cgColor,
  UIColor.white.cgColor,
  UIColor.black.cgColor
]
gradientLayer.colors = colors

The gradient above starts with a black color, blends to white, and finally blends back to black.

You can also specify where exactly in the gradient’s frame these colors should appear. Add the following code below:

let locations: [NSNumber] = [
  0.25,
  0.5,
  0.75
]
gradientLayer.locations = locations

This sets up the gradient color milestones as follows:

You can have as many key points and color milestones as you like, but the text gradient animation in this chapter only needs the simple black-white-black gradient shown above.

Add the following code to layoutSubviews() to give the gradient a frame:

gradientLayer.frame = bounds

All that you need to do now is add the gradient to the view’s layer to see it in action. Add the line of code below to the end of didMoveToWindow():

layer.addSublayer(gradientLayer)

Build and run your project; you should see the app display the exact gradient you’re looking for:

That’s a great start! Now you need to figure out how to animate this gradient.

Animating gradients

CAGradientLayer offers you four animatable properties along with the ones inherited from CALayer:

  • colors: Animate the gradient’s colors to give it a tint.
  • locations: Animate the color milestone locations to make the colors move around inside the gradient.
  • startPoint and endPoint: Animate the extents of the layout of the gradient.

In this section you’ll animate locations to make the gradient “move”.

Add the following code to the end of didMoveToWindow():

let gradientAnimation = CABasicAnimation(keyPath: "locations")
gradientAnimation.fromValue = [0.0, 0.0, 0.25]
gradientAnimation.toValue = [0.75, 1.0, 1.0]
gradientAnimation.duration = 3.0
gradientAnimation.repeatCount = Float.infinity

In this layer animation, you begin by pushing the three color milestones to the left edge of the gradient’s frame and end the animation with all three pushed towards the right edge:

The animation lasts 3 seconds and will repeat forever since you set repeatCount to infinity.

Finally, add the following line to the end of didMoveToWindow():

gradientLayer.add(gradientAnimation, forKey: nil)

This will add the animation to the gradient layer. Build and run your project and you’ll see the animation take shape:

This looks pretty nice, but the gradient is quite harsh, especially near the middle. No problem: just enlarge the gradient bounds and you’ll get a much gentler gradient.

Find the line gradientLayer.frame = bounds in layoutSubviews() and replace it with the following code that sets a much larger frame for the gradient layer:

gradientLayer.frame = CGRect(  
  x: -bounds.size.width,     
  y: bounds.origin.y,   
  width: 3 * bounds.size.width,     
  height: bounds.size.height)

This sets the gradient frame to three times the width of the visible area. The animation enters the view, passes right through it, and exits out the right hand side:

Build and run your project to see what your changes look like:

That looks more like the smooth gradient you’re going for. Now that you have the gradient, you’ll need to create the text layers to use as a mask.

Creating a text mask

In this section you’ll render the string stored in the text property of AnimatedMaskLabel and use that to mask the gradient layer. Create a new constant property inside the AnimatedMaskLabel class to hold the text attributes as follows:

let textAttributes: [NSAttributedString.Key: Any] = {
  let style = NSMutableParagraphStyle()
  style.alignment = .center
  return [
    .font: UIFont(
      name: "HelveticaNeue-Thin",
      size: 28.0)!,
    .paragraphStyle: style
  ]
}()

Next you need to render the text as an image. A natural place to do this is in the property observer for the text property. Add the following code after the setNeedsDisplay() call:

let image = UIGraphicsImageRenderer(size: bounds.size)
  .image { _ in
    text.draw(in: bounds, withAttributes: textAttributes)
}

Here you use an image renderer to set up a context, draw to it, and get the results out as a UIImage. Now you can use that image to create a mask on your gradient layer.

To do that, first create a layer out of the image as follows:

let maskLayer = CALayer()
maskLayer.backgroundColor = UIColor.clear.cgColor
maskLayer.frame = bounds.offsetBy(dx: bounds.size.width, dy: 0)
maskLayer.contents = image.cgImage

You create maskLayer as an empty layer simply by using the default initializer of CALayer. You then set a fully transparent layer background since you’re going to use the layer as a mask. Then you offset the layer frame by the width of the view; this way, the mask will show up in the center of the gradient. This is necessary as your “stretched” gradient is currently three times as wide as the visible view. Finally, you assign the image object directly to the contents property of the layer.

Add one more line to set the new layer as a mask for the gradient:

gradientLayer.mask = maskLayer

Build and run your project to see the fully developed animation in action:

Hey — that looks really slick! But you haven’t yet discovered what’s revealed when the user swipes across the label — and are you limited to a monochrome palette for your gradient? All will be revealed — as you work through the challenges below!

Key points

  • You can draw gradients on screen by using the CAGradientLayer and setting the gradient colors.
  • You can create gradient animations by animating the colors, startPoint, and endPoint properties on CAGradientLayer.
  • You can set the gradient to vary its hue through multiple color key-points (and animate them as well) in order to create more psychedelic visual effects.

Challenges

I know the suspense is killing you; these two challenges will add a slide gesture recognizer to the label and add one additional color effect to the gradient animation.

Challenge 1: Slide to reveal gesture recognizer

Open ViewController.swift and add the following code to viewDidLoad():

let swipe = UISwipeGestureRecognizer(target: self,
  action: #selector(ViewController.didSlide))
swipe.direction = .right
slideView.addGestureRecognizer(swipe)

This creates a slide-to-right gesture recognizer and attaches it to slideView. The recognizer will call didSlide() on ViewController.

didSlide() is already implemented for you, so all you need to do now is fire up the application and slide your finger over the animating label to reveal what lies beneath.

Challenge 2: Psychedelic gradient animations

In the final challenge of this chapter you’ll experiment with adding more colors to the gradient and observe the effects.

If you’d like something to guide your exploration, try using the following list of colors for your gradient:

UIColor.yellow
UIColor.green
UIColor.orange
UIColor.cyan
UIColor.red
UIColor.yellow

You’ll need to adjust the locations values as well to keep all of your colors in order. Start your animation with the following locations: 0.0, 0.0, 0.0, 0.0, 0.0 and 0.25. Then animate the locations to: 0.65, 0.8, 0.85, 0.9, 0.95 and 1.0.

Build and run your project; play around with the animation and the various parameters until you have it tuned it to your own design preferences:

This brings the chapter to a close; you’ve seen how easy it is to animate gradients as well as how to implement some advanced layer masking tricks with text layers.

The next chapter covers stroke animations for shapes, which is the final topic you’ll cover in this section of the book on layer animations; you’ll learn how to draw shapes interactively, and as a bonus, you’ll cover advanced keyframe animations.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.