NEW: Master Swift design patterns with my latest book! >>

< Previous: Preparing for action   Next: Transform: CGAffineTransform >

Switch, case, animate: animate(withDuration:)

The currentAnimation property can have a value between 0 and 7, each one triggering a different animation. You might be tempted to write code like this:

if currentAnimation == 0 {
    anim1()
} else if currentAnimation == 1 {
    anim2()
} else if currentAnimation == 2 {
    andSoOn()
}

But that's not a very efficient way of checking a variable for multiple possible values, so programming languages have a different syntax for doing exactly that, known as switch/case. Using this syntax, we could more or less rewrite the previous code like this:

switch currentAnimation {
case 0:
    anim1()
case 1:
    anim2()
case 3:
    andSoOn()
}

I say "more or less rewrite" because Swift wants to make sure your code is as safe as possible, and one of the checks it executes is that your switch/case statements are exhaustive – that every possible outcome is catered for.

As a result, you will frequently need to include a default case block to match any value not explicitly catered for above, like this:

switch currentAnimation {
case 0:
    anim1()
case 1:
    anim2()
case 3:
    andSoOn()
default:
    break
}

The break statement exits the switch/case block, so the default case effectively does nothing.

Note: by default, Swift executes only the case block that matches the value you are switching on. If you want it to carry on executing the next one as well, you should use the fallthrough statement. If you don't know what this means, you don't want it!

We're going to create a big switch/case block inside tapped(), but we're going to start small and work our way up – the default case will handle any values we don't explicitly catch.

This switch/case statement is going to go inside a new method of the UIView class called animate(withDuration:), which is a kind of method you haven't seen before because it actually accepts two closures. The parameters we'll be using are how to long animate for, how long to pause before the animation starts, any options you want to provide, what animations to execute, and finally a closure that will execute when the animation finishes.

Because the completion closure is the final parameter to the method, we'll be using trailing closure syntax just like we did in project 5.

Update your tapped() method to this:

@IBAction func tapped(_ sender: Any) {
    tap.isHidden = true

    UIView.animate(withDuration: 1, delay: 0, options: [],
       animations: { [unowned self] in
        switch self.currentAnimation {
        case 0:
            break

        default:
            break
        }
    }) { [unowned self] (finished: Bool) in
        self.tap.isHidden = false
    }

    currentAnimation += 1

    if currentAnimation > 7 {
        currentAnimation = 0
    }
}

That won't do anything yet, which is remarkable given that it's quite a lot of code! However, it has put us in a position where we can start dabbling with animations. But first, here's a breakdown of the code:

  • When the method begins, we hide the tap button so that our animations don't collide; it gets unhidden in the completion closure of the animation.
  • We call animate(withDuration:) with a duration of 1 second, no delay, and no interesting options.
  • For the animations closure we first do the usual [unowned self] in dance to avoid strong reference cycles, then enter the switch/case code.
  • We switch using the value of self.currentAnimation. We need to use self to make the closure capture clear, remember. This switch/case does nothing yet, because both possible cases just call break.
  • We use trailing closure syntax to provide our completion closure. This will be called when the animation completes, and its finished value will be true if the animations completed fully.
  • As I said, the completion closure unhides the tap button so it can be tapped again.
  • After the animate(withDuration:) call, we have the old code to modify and wrap currentAnimation.

If you run the app now and tap the button, you'll notice it doesn't actually hide and show as you might expect. This is because UIKit detects that no animation has taken place, so it calls the completion closure straight away.

Get six books for only $150

The Swift Power Pack includes my first six books for one low price, helping you jumpstart a new career in iOS development – check it out!

< Previous: Preparing for action   Next: Transform: CGAffineTransform >
Click here to visit the Hacking with Swift store >>