Day 57 - 100 Days of Swift

4 minute read

Project 15 (part 1)

Day 57 is the first part of the fifteenth project. It is a technique project on animation using UIView.animate. You look at scaling, translating and rotating with CGAffineTransform and you look at animating other properties on a UIView. You also look at a couple of basic options you can add to an animation.

First, you use the storyboard to give yourself a button at the bottom center of the screen (it doesn’t really matter where), tie it to an action in ViewController.swift and then add a couple of variables:

1
2
3
4
5
var imageView: UIImageView!
var currentAnimation = 0

@IBAction func tapped(_ sender: UIButton) {
}

You set up imageView in viewDidLoad:

1
2
3
imageView = UIImageView(image: UIImage(named: "penguin"))
imageView.center = CGPoint(x: 512, y: 384)
view.addSubview(imageView)

Then, at the end of tapped() you add this code to rotate through the current animation:

1
2
currentAnimation += 1
currentAnimation %= 8

This adds 1 to currentAnimation and then sets it to the results of currentAnimation % 8. This is a clean way of cycling through the numbers 0-7.

Next, you add a call to UIView.animate with a switch statement to perform the different animations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sender.isHidden = true

UIView.animate(withDuration: 1,
               delay: 0,
               options: [],
               animations: {
    switch self.currentAnimation {
    case 0:
        break
    default:
        break
    }
}) { _ in
    sender.isHidden = false
}

Right now this doesn’t actually do anything, but once we add the animations, it will hide the button, perform the animation, and then unhide the button.

The first animation you add is to scale up the image view and scale it back to it’s original size:

1
2
3
4
case 0:
    self.imageView.transform = CGAffineTransform(scaleX: 2, y: 2)
case 1:
    self.imageView.transform = .identity
Gif of basic scale animation.

Then you add a translation:

1
2
3
4
5
case 2:
    self.imageView.transform = CGAffineTransform(translationX: -256,
                                                 y: -256)
case 3:
    self.imageView.transform = .identity
Gif of basic translate animation.

Then you add a rotation:

1
2
3
4
case 4:
    self.imageView.transform = CGAffineTransform(rotationAngle: CGFloat.pi)
case 5:
    self.imageView.transform = .identity
Gif of basic rotate animation.

And finally, you animate the opacity and background color at the same time:

1
2
3
4
5
6
case 6:
    self.imageView.alpha = 0.1
    self.imageView.backgroundColor = .green
case 7:
    self.imageView.alpha = 1
    self.imageView.backgroundColor = .clear
Gif of basic opacity and color animation.

The only modification I made was to get rid of cases 1, 3, and 5 and make the default case be to reset the identity:

1
2
default:
    self.imageView.transform = .identity

This just gets rid of a little bit of the duplicate code.

Finally, you add a little bit of spring to the animation like this:

1
2
3
4
5
6
UIView.animate(withDuration: 1,
               delay: 0,
               usingSpringWithDamping: 0.5,
               initialSpringVelocity: 5,
               options: [],
               animations: {

That leads to animations that look like this:

Gif of spring scale animation.
Gif of spring translate animation.
Gif of spring rotate animation.
Gif of spring opacity and color animation.

You can find my version of this project at the end of day 57 on Github here.

Reflections

Animations are a lot of fun. They can definitely bring a lot of whimsy to an app, if they are implemented well and don’t keep the user from getting their work done. The main thing that usually keeps me from doing many animations is that it seems like it will be a lot of work. I have some experience with animation outside of the programming environment (mostly with After Effects), and I know how long it can take to dial in those keyframes. But it is amazing how easy some things can be with the system APIs. These animations all look great (with the exception of the opacity/color one, which I think is mostly there for demonstration purposes) and it took very little code to get them up and running. That’s awesome!