watchOS 4 Tutorial Part 3: Animation

In this third part of our watchOS 4 tutorial series, learn how to add animations into your watchOS app! By Audrey Tam.

Leave a rating/review
Save for later
Share
Update Note: This tutorial has been updated to Swift 4/watchOS 4 by Audrey Tam. The original tutorial was written by Mic Pringle.

In the first part of this series, you learned about the basics of watchOS 4 development by creating your first interface controller.

In the second part of the series, you learned how to add tables to your app.

In this third part of the series, you’ll learn how to use watchOS 4 animations by adding a new check-in interface to your app.

In the process, you’ll learn:

  • How to create image-based animations.
  • How to use the watchOS 4 animation API.

And with that, let’s get cracking! ┗(°0°)┛

Note: This tutorial picks up where we left things off in Part 2 of this series. You can either continue with the same project, or download it here, if you want to start fresh.

Getting Started

Open Watch\Interface.storyboard, and drag an interface controller from the Object Library onto the storyboard canvas. With the interface controller selected, open the Attributes inspector, and set Identifier to CheckIn. You do this so you can present the interface controller from within ScheduleInterfaceController.

Next, drag a group onto the new interface controller from the Object Library. Use the Attributes inspector to make the following changes:

  • Set Layout to Vertical.
  • Set Mode to Center.
  • Set the Horizontal Alignment to Center.
  • Set Height to Relative to Container.

Your interface controller should now look like this:

Background-Group

Next, you’ll build the same label-image-label group that you created for the table row.

Drag another group into the existing group, and make the following changes using the Attributes inspector:

  • Set Spacing to 4.
  • Set the Horizontal Alignment to Center.
  • Set Width to Size To Fit Content.
  • Set Height to Fixed, with a value of 30 (a little shorter than the table row).

Add a label and an image to this new layout group. You’ll configure the label, then copy and update it, to display the origin and destination of each flight.

Select the image, either in the storyboard or in the document outline. Using the Attributes inspector, make the following changes:

  • Set Image to Plane.
  • Set Tint to #FA114F (our pink, again!).
  • Set Vertical Alignment to Center.
  • Set Width to Fixed, with a value of 24.
  • Set Height to Fixed, with a value of 20.

As before, the image isn’t tinted, so you can’t see it against the black background of the interface controller. But you know it’s there.

Select the label, and set its Text to MEL. Next, change its Font to System, with a style of Semibold and a size of 20. Finally, set its Vertical alignment to Center, and check that its Width and Height are both Size To Fit Content.

Copy the label, then paste it next to the image. Change its text to SFO and its Horizontal Alignment to Right. Your interface controller should now look like the following:

Upper-Group-Complete

Now it’s time to add a huge check-in button!

Adding the Check-In Button

Drag a button from the Object Library onto the interface controller, making sure it’s positioned as a sibling of the group containing the origin and destination labels:

Button-Position

Buttons in WatchKit are incredibly flexible; you can use them with their stock appearance – the way the one you just added looks – or you can turn them into a layout group, and add other interface objects to customize their appearance. That’s exactly what you’re going to do here.

Select the button, and use the Attributes inspector to make the following changes:

  • Set Content to Group.
  • Set the Horizontal Alignment to Center.
  • Set the Vertical Alignment to Center.

Your interface controller should now look like this:

Button-Group

You may have noticed when you changed the Content attribute of the button, a new group appeared in the document outline:

Button-Embedded-Group

This is what you’re going to use as the background of your custom check-in button. Select this group, and use the Attributes inspector to make the following changes:

  • Set Color to #FA114F.
  • Set Radius to 39.
  • Set Width to Fixed, with a value of 78.
  • Set Height to Fixed, with a value of 78.

The interface controller should now look like this:

Round-Button

Your check-in button is really starting to take shape. The only thing missing is the label, so you’ll add that next.

Drag a label from the Object Library into the group belonging to the button, and then select it. Once again, make the following changes, using the Attributes inspector:

  • Set Text to Check In.
  • Set Font to System, with a style of Semibold and a size of 16.
  • Set the Horizontal Alignment to Center.
  • Set the Vertical Alignment to Center.

Your finished check-in interface controller should now look like this:

Check-In-Interface-Complete

With the interface complete, it’s now time to create a subclass of WKInterfaceController to manage this controller, and to update ScheduleInterfaceController to show it.

Creating the Controller

Right-click on the Watch Extension group in the Project navigator and choose New File…. In the dialog that appears, select watchOS\Source\WatchKit Class, and click Next. Name the new class CheckInInterfaceController, and make sure it’s subclassing WKInterfaceController, and that Language is set to Swift:

File-Options

Click Next, and then Create.

When the new file opens in the code editor, delete the three empty method stubs, so you’re left with just the import statements and the class definition.

Next, add the following to the top of the class:

@IBOutlet var backgroundGroup: WKInterfaceGroup!
@IBOutlet var originLabel: WKInterfaceLabel!
@IBOutlet var destinationLabel: WKInterfaceLabel!

Here, you’re simply adding outlets for the outer-most group and the two labels of the interface you just created. You’ll hook everything up soon.

Next, add the following just below the outlets:

var flight: Flight? {
  didSet {
    guard let flight = flight else { return }

    originLabel.setText(flight.origin)
    destinationLabel.setText(flight.destination)
  }
}

You know the score by now! Here, you’ve added an optional property of type Flight, which includes a property observer. When the observer is fired, you try to unwrap flight, and if successful, use flight to configure the two labels. This is all familiar territory now, right?

Now you just need to set flight when the controller is presented. Add the following to CheckInInterfaceController:

override func awake(withContext context: Any?) {
  super.awake(withContext: context)

  if let flight = context as? Flight {
    self.flight = flight
  }
}

Again, this should be super familiar by now. You try to unwrap and cast context to an instance of Flight. If that succeeds, you use it to set self.flight, which in turn, triggers the property observer configuring the interface.

Finally, add the following action just below awake(withContext:):

@IBAction func checkInButtonTapped() {
  // 1
  let duration = 0.35
  let delay = DispatchTime.now() + (duration + 0.15)
  // 2
  backgroundGroup.setBackgroundImageNamed("Progress")
  // 3
  backgroundGroup.startAnimatingWithImages(in: NSRange(location: 0, length: 10), 
    duration: duration,
    repeatCount: 1)
  // 4
  DispatchQueue.main.asyncAfter(deadline: delay) { [weak self] in
    // 5
    self?.flight?.checkedIn = true
    self?.dismiss()
  }
}

Here’s the play-by-play of what’s happening:

  1. You create two constants: one for the duration of the animation and one for the delay after which the controller will be dismissed. Instead of being a Double, delay is an instance of DispatchTime, since you’ll be using it with Grand Central Dispatch.
  2. You load a sequence of images named Progress, and set them as the background image of backgroundGroup. Layout groups conform to WKImageAnimatable, which allows you to use them to play back animated image sequences.
  3. You begin playback of the image sequence. The range you supply covers the entire sequence, and a repeatCount of 1 means the animation will play just once.
  4. WatchKit doesn’t have completion handlers, so you use Grand Central Dispatch to execute the closure after the given delay.
  5. In the closure, you mark flight as checked-in, and then dismiss the controller.

Now you just need to add the images to the project, and hook up the outlets and single action.

Download this zip file, unzip the file, and drag the folder into your Watch\Assets.xcassets.

Make sure you drag the folder, not its contents. This should create a new group in the asset catalog called Progress, containing several image sets:

Progress-Image-Group

With the images in place, it’s time to set up the outlets and button action.

Open Watch\Interface.storyboard, and select your new interface controller. In the Identity inspector, change Custom Class\Class to CheckInInterfaceController:

Custom-Class

Next, in the document outline, right-click on CheckIn to invoke the outlets and actions popup. Connect backgroundGroup to the outer-most group in the interface controller:

Background-Group-Outlet

In the storyboard canvas, connect destinationLabel to the label containing SFO, and connect originLabel to the label containing MEL.

Next, connect checkInButtonTapped to the big, round, pink button:

Connect-Action-2

Before you build and run, the final change you need to make is to actually present this interface controller.