Make App Pie

Training for Developers and Artists

Make a WatchOS 3 Haptic Catalog with a Picker

How does the Apple Watch communicate to the user when they are not looking at the watch face? That is done with haptics. Haptics are sounds and taps letting the user know something is happening. In this lesson, I’ll explain to you how to use haptics in Watch OS applications by creating a catalog of all the haptics using a WatchOS picker.

The picker control is an object for selection of multiple items. You can use the picker to display either full-screen images or text for selection. In this lesson, I’ll keep it simple with a text-based picker.

Make a New Project

Create a new WatchOS project called HapticPickerDemo. Use Swift as the language, uncheck Notifications, and keep the device Universal.  Open the interface.storyboard file in the WatchKit App group.

Find the picker in the object library. Drag a picker to the interface.

2016-08-27_07-01-54

With the picker selected you’ll see four attributes of a
picker.  The Focus Style and Indicator give you visual elements to highlight the picker. The most important attributes are Style and of course Enabled. You have three style choices: List, Stack and Sequence. List is a text-based picker. Stack and Sequence is an animated and non-animated image picker.

2016-08-27_07-09-18

Set your attributes to the illustration above.  Set the picker’s Style  to List. Set the Focus Style to  Outline with Caption. This will show an outline around the picker with a caption at the top. This caption is context sensitive to the picker selection. If you’ve set a complication on a watch, you are familiar with this focus style, such as  the black on green lettering  for weather in this setting:

2016-08-27_07-02-50

From the Object Library, add  a button under the picker. Title the button Play Haptic.

2016-08-27_07-04-29

Close the attributes and navigation panels. Open the assistant editor.

Make an outlet for the picker by control dragging from the picker to the InterfaceController class

@IBOutlet var picker: WKInterfacePicker!

Make an action for the picker’s action,changing Outlet to Action in the popup after you control drag.

 func pickerDidChange(_ value: Int) {
    }

Make an action for the button’s action, changing Outlet to Action in the popup after you control-drag.

 func playHaptic(_ value: Int) {
    }

Playing a Haptic

There’s two parts to a haptic you need to know. There is a play function [in WatchOS 2 playHaptic()]  you call on the current device object called by
WKInterfacedevice.current()object [in WatchOS 2 WKInterfaceDevice.currentdevice() ]. The play method has one parameter of type WKHapticType, which is an enumeration of haptic types.

  • notification tells the user there is a notification,
  • directionUp indicates an upward value,
  • directionDown indicates a downward value,
  • success indicates the successful completion of a task,
  • failure a failed task,
  • retry tells the user to retry,
  • start the beginning of an action,
  • stop the end of an action,
  • click is a very slight click, which you probably won’t hear but probably feel on a watch.

Close the Assistant editor.  Go to InterfaceController.swift in the App extension group. To make the button play a .success haptic change the playHaptic action to this:

 
func playHaptic(_ value: Int) {
    WKInterfaceDevice.current().play(.success)
}

Build and run using the 38mm simulator. Press the Play Haptic Button and  you get a sound.

In the simulator, you don’t get the taps you’ll get if you were using the watch. If you have a watch, run this on your watch and you’ll feel the tap.

Implementing the Picker

There is no documentation on what the taps feel like. It’s hard to describe in words,you have to feel it. In the rest of this lesson we’ll use WatchKit’s WKInterfacePicker to make a selectable catalog of haptics you can run on an Apple Watch.

Pickers have one parameter. Since pickers hold the list in a sequence, the parameter value is the index of that sequence. Delete all life cycle methods but willActivate. Add the list of haptic types exactly in the order above for the picker as an array.

let titles = [
    "notification","directionUp",
    "directionDown","success",
    "failure","retry",
    "start","stop","click"
]

Pickers will not use these arrays directly. Pickers get their selections from a WKPickerItem object. Code will create an array of WKPickerItem, and set that as the picker’s items. WKPickerItem has several properties to make the picker flexible.

  • title – A String? to use in a list
  • caption – A String? used as a caption for item in a list
  • accessoryImage – A Small WKImage? to display next to title in a
    list or as an alternate to text as in small complication setting.
  • contentImage – In a Stacked or Image Sequence style, a
    WKImage?

The picker has a setItems method which takes the array of picker
items to make the list, stack, or sequence of images, depending on the display style. For every picker you create, you build a function that iterates through the arrays, adding the elementsts as pickerItems. Add a function refreshPickerItems:

 func refreshPickerItems(){
 }

Add an empty array of picker items to the function.

 
func refreshPickerItems(){
    var pickerItems:[WKPickerItem] = []
}

Add a loop iterating through the titles.

 
func refreshPickerItems(){
    var pickerItems:[WKPickerItem] = []
    for item in titles{
    }
}

In the loop creates an instance of WKPickerItem. Add to pickerItem the title  from the corresponding element of the arrays. Add the literal caption Haptic. (If you want to experiment, with captions, try coding it as "Haptic-" + item ) Add the picker item to the pickerItems array.

func refreshPickerItems(){ 
    var pickerItems:[WKPickerItem] = [] 
    for item in titles{ 
        let pickerItem = WKPickerItem()
        pickerItem.title = item
        pickerItem.caption = "Haptic"
        pickerItems += [pickerItem]
    }
}

Once the pickerItems array is complete, set the items in the picker.

func refreshPickerItems(){ 
    var pickerItems:[WKPickerItem] = [] 
    for item in titles{ 
        let pickerItem = WKPickerItem()
        pickerItem.title = item
        pickerItems += [pickerItem]
    }
    picker.setItems(pickerItems)
}

In willActivate, call this function to refresh the list.

override funcwillActivate() {
    super.willActivate()
    refreshPickerItems()
}

Selecting a Picker Entry

If you were to run the project now, you would see the picker displayed correctly. You could not select anything though. That happens in the action. The pickerDidChange action has a Int parameter value, which is the current index on the picker.

I told you earlier to be careful getting the order correct in the titles  array.  This is why. Since WKHapticType is a enum you can select the type by the rawValue of the enum. That raw value matches the value parameter. Before we try this, add a property hapticType to store the type.

varhapticType:WKHapticType = .notification

I’ve initially set this to .notification. In the pickerDidChange action set the hapticType property with the raw
value we can get the value parameter.

@IBAction func pickerDidChange(_ value: Int) { 
    hapticType = WKHapticType(rawValue:value)!
}

Playing the Haptic

The last step is to play the haptics. To the action  pickerDidChange  add a .click haptic

WKInterfaceDevice.current().play(.click)

This demonstrates one use for haptics, feedback to the user.  In the action playHaptic, change the .success haptic to the property hapticType

 @IBAction fund playHaptic() {
        WKInterfaceDevice.current().play(hapticType)
    }

Run in the 38mm simulator. Select a haptic and press Play Haptic. You’ll hear the chime for it.

If you have a watch, load it on your watch, and try the application. When you tap Play haptic on the watch you will get the watch tap as well.

Don’t over use Haptics. Unless your app is an interval timer, don’t  have one go off every second or minute. That just gets annoying. Use haptics at the right times to attract a user’s attention to their Apple watch or give instant feedback.

The Whole Code

//
//  InterfaceController.swift
//  HapticPickerDemo WatchKit Extension
//
//  Created by Steven Lipton on 8/26/16.
//  Copyright © 2016 Steven Lipton. All rights reserved.
//

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController {

    @IBOutlet var picker: WKInterfacePicker!
    
    //List of WKHapticType in rawValue order
    let titles:[String] = [
        "notification","directionUp",
        "directionDown","success",
        "failure","retry",
        "start","stop","click"]
    
    var hapticType:WKHapticType = .notification
    
    //Use the index from the picker as the rawValue for the WKHapticType
    @IBAction func pickerDidChange(_ value: Int) {
        hapticType = WKHapticType(rawValue: value)!
        WKInterfaceDevice.current().play(.click)
    }
    
    
    @IBAction func playHaptic(){
        WKInterfaceDevice.current().play(hapticType)
    }
    
    
    func refreshPickerItems(){
        var pickerItems:[WKPickerItem] = []
        for item in titles{
            let pickerItem = WKPickerItem()
            pickerItem.title = item
            pickerItem.caption = "Haptic"
            pickerItems += [pickerItem]
        }
        picker.setItems(pickerItems)
    }
    
    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
        refreshPickerItems()
       
        
    }

}

2 responses to “Make a WatchOS 3 Haptic Catalog with a Picker”

  1. WKInterfaceDevice.currentDevice().playHaptic(hapticType)

    1. That is true for about another three weeks. The method play() is the WatchOS3/Xcode 8 version. I’ve noted that in the text above. Thanks for the catch.

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 )

Facebook photo

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

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: