Swift Watchkit: Adding Context Menus

2015-07-01_06-58-23Menus are cool. Since the first interactive program, there have been menus in applications. The Apple Watch is no exception, but has no space to put a menu. To solve this problem, Apple uses the new force touch gesture to pop open a menu. You can make your own menu, with customized icons and captions. Like much of WatchKit, it’s simple to make a menu. However it’s not so simple to make an icon. In this lesson, we’ll show you what you need to know to make menus with your own custom icons.

Making Icons for WatchOS Menus

WatchOS menu icons share a lot with iOS tab bar icons. They are relatively small, and they do not use colors like images. Instead they use the transparency channel, also known as the alpha channel to make a shade of gray.  As you can see in this illustration, 100% alpha is black and 0% alpha is white. Any value in between is a shade of gray.

alpha color

Icons do not care about color. They are always one color. In WatchOS menus, that is a shade of gray.  If we use the icon in some other part of our interface, we can see it in color, but the system will make the grayscale icon for the menu icon.

WatchKit is very particular about the sizes of icons. An icon needs to be on an transparent image 120 pixel square for 42mm and 104 pixel square for a 38mm. The icon image should be 53px square for a  42mm watch and  40px square for a 38mm  watch.  You can make a icon bigger that that, but it is not recommended for good looks.  To summarize all this, we have this handy guide:

Apple watch Menu Icon template guide

Developers and graphic designers have different ideas of what application to use for making icons.  Some go for the raster graphic route using Photoshop or its open source counterpart Gimp. Some like me prefer vector graphics. For vector graphics there is the free open source application Inkscape. For my workflow, I paid around $30 for iDraw by Indeeo, since it has linked versions for both Mac and iPad. I can work on either device.

image    2015-07-02_06-11-05

I’ve made three icons for this lesson, saving each as a .png file. One was a camera icon I modified from iDraw’s clipart, an undo button and a cancel button.

  IconCamera42@2x IconCancel42@2x IconUndo42@2x

I also made a copy for the  38mm watch:

 IconCamera38@2x IconCancel38@2x IconUndo38@2x

Right click  each icon above and save it. You can also download all of them here: WatchMenuIcons.zip for use  in the rest of the exercise.

To demonstrate alpha values, I set the top of the camera body and the lens of the camera at different alpha values.

camera42@2x annotated

 If you are only using lines for your graphics, you need thick lines to be seen clearly on the menu. Make sure you use thick lines if you make line graphics such as the Undo or Cancel icons. The icon guide  has a table of Apple’s recommended line weights.

Create the Project

In Xcode, make a new single-view project called SwiftWatchMenuIcons. You need the language set to Swift and device as Universal. Save the project.

Once the project loads, select Edit>New Target from the drop down menu. Add a WatchKit App. You will not need a notification for this project, so you can turn that off.  Make sure the language is Swift. Click Finish, and then Activate.

Add Icons to Xcassets

In the WatchKit App group, click on the Images.xcassets folder.  This opens up the assets folder.  I haven’t found if Apple came up with a naming convention for watch images for automatic loading,  so we’ll add them manually. At the bottom of the assets folder you will find an add icon(+)

2015-07-02_06-51-52

Click the Icon and you get a menu. Select New Image Set.

2015-07-02_06-53-46

Xcode will list a new image set under your app icon image set.  Repeat this process two more times to get three image sets.

2015-07-02_06-54-54

 Click one of the image sets. In the attributes inspector, change its name to Camera.  You’ll notice it gives you a generic image set

2015-07-02_06-59-15

You’ll notice under the name is a dropdown called Device. Change it from its current setting of Universal to Device Specific. You will get a set of check boxes. Uncheck iPhone and iPad, so only Apple Watch is selected.

2015-07-02_06-58-28

The image set changes to Apple Watch.

2015-07-02_06-59-42

Open your finder window where you saved the icon images. Drag from the 42mm camera icon to the spot marked for a  42 mm image in the assets folder and from the 38mm camera icon to the spot for the 38mm image.

2015-07-02_07-02-27

When done you’ll have two icons in the image set:

2015-07-02_07-05-40

Now repeat this for the other two image sets, naming them Cancel and Undo, then moving in the icons for Cancel and Undo. When done you should have three image sets like this:

2015-07-02_06-55-54

Add a Menu to the Storyboard

Go to the storyboard for the watch app. Find the menu in the object browser:

2015-07-06_05-27-55

Drag the menu into the interface:

2015-07-06_05-30-10

 

There is no visible change, unless you look at the document outline. If you do not have it open, click the 2015-07-06_05-31-05 icon at the bottom of the storyboard. In the panel that appears on the left, you will see the menu in the outline.

2015-07-06_05-33-19

This menu has one menu item, we need three. There are two ways to add menu items. The first is to  drag a menu item from the object library to the document outline.

2015-07-06_05-41-13

The second is to select the menu and change the properties to 3. There is a maximum of four menu items.

2015-07-06_05-45-03

In the document outline, select the top menu item.

2015-07-06_05-50-12

The attributes inspector shows this:

2015-07-06_05-50-47

Change the Title to Camera. Making sure the image type is Custom. Click the drop down for the image, and change it to Camera:

2015-07-06_05-59-10

Repeat for the other two menu items changing them to Cancel and Undo. When done, your document outline should look like this:

2015-07-06_06-09-28

Connect the Menu Items.

Like buttons, menu items have actions in their interface controller.  Unlike buttons we cannot drag from the button on the storyboard. Instead, we need to set them from the document outline.

Open the assistant editor.  Control drag from the menu item Camera in the document outline to the code in the assistant editor.

2015-07-06_06-05-06

Create an action named cameraPressed.

2015-07-06_06-10-59

Do the same for the Cancel and Undo buttons, creating the following code:

    @IBAction func cameraPressed() {
    }
    @IBAction func undoPressed() {
    }
    @IBAction func cancelPressed() {
    }

Our buttons will display a message on the watch when pressed. We need two more objects for that. Drag an image and a label to the interface. Position the image center and top. Position the label center and bottom. Your interface should look like this:
2015-07-06_06-24-01Control-drag from the label  to the code. Make an outlet named displayLabel. Control-drag from the image to the code make an outlet named displayImage.

Add Code for the Menus

For starters, we’ll make a method to display the icon and the text. Watchkit can’t read properties, so we have to get sneaky. Add this to your code:

func displayAction(action:String){
    displayLabel.setText(action)
    if let image = UIImage(named: action){
        displayImage.setImage(image)
    }else{
        displayLabel.setText("No Image " + action)
    }
}

We’ll read a string parameter action which will be our action. First, we display the menu button pressed on the label. Next we assign a UIImage with the same name as our action to image. If image is non-nil, we have a image and we set the image in displayImage. If the value of Image is nil, we didn’t find the image, and we display  a No Image message.

Change our three action methods to this:

@IBAction func cameraPressed() {
        displayAction("Camera")
    }
    @IBAction func undoPressed() {
        displayAction("Undo")
    }
    @IBAction func cancelPressed() {
        displayAction("Cancel")
    }

We tell the displayAction method which action to display. It appears we are ready to run.

Run the App, Find a Bug

Build and run. We start with a blank watch:

Photo Jul 07, 5 56 37 AM

Force touch on the watch or click and hold on the simualtor. We get our menu:

Photo Jul 07, 5 56 43 AM

Notice the camera has shades of gray in the lens and top of the camera due to our alpha channel changes.  Press the camera menu item and we get this.

Photo Jul 07, 5 56 50 AM

We do run the camera action but the watch can’t find an image.  I made this error intentionally to underline a very common mistake with WatchKit. There are two asset libraries: One for the app and one for the extension. If you reference an image in the interface, you need the image in the WatchKit App bundle. If you reference the image in your code, then you need to reference the image in your WatchKit Extension.

2015-07-07_06-24-37

Just like we did above to add the icons to the app,  add the icons to the extension.

2015-07-07_06-28-24

Build and run. Open the menu with a force touch, and select the camera.

Photo Jul 07, 6 36 44 AM

 Now we have a camera. Notice the camera is yellow like the original image, since this is an image, not an icon. The lens and camera top are darker in color. That again is the alpha value. They are darker than the body of the camera because they are letting the black background color through. This was the reason I made the icon yellow. If you plan to reuse your icons as images in your extension, make them a color that contrasts well with black.  If you made black icons, they would be black on black images, rendering them invisible. You might  spend hours looking at code trying to find a bug of missing images when your images are merely camouflaged.

In our next WatchKit lesson we’ll start working with the last of the WatchKit controls: table views.

The Whole Code

//
//  InterfaceController.swift
//  SwiftWatchMenuIcons WatchKit Extension
//
//  Created by Steven Lipton on 7/2/15.
//  Copyright (c) 2015 MakeAppPie.Com. All rights reserved.
//

import WatchKit
import Foundation

class InterfaceController: WKInterfaceController {

    @IBOutlet weak var displayLabel: WKInterfaceLabel!

    @IBOutlet weak var displayImage: WKInterfaceImage!

    @IBAction func cameraPressed() {
        displayAction("Camera")
    }
    @IBAction func undoPressed() {
        displayAction("Undo")
    }
    @IBAction func cancelPressed() {
        displayAction("Cancel")
    }

    func displayAction(action:String){
        displayLabel.setText(action)
        if let image = UIImage(named: action){
            displayImage.setImage(image)
        }else{
            displayLabel.setText("No Image " + action)
        }
    }
}

2 thoughts on “Swift Watchkit: Adding Context Menus”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s