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.
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.
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:
From the Object Library, add a button under the picker. Title the button Play Haptic.
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
– AString?
to use in a listcaption
– AString?
used as a caption for item in a listaccessoryImage
– A SmallWKImage?
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() } }
Leave a Reply