[Updated for WatchOS5 Beta 6/22/18]
On the Apple Watch, the digital crown seems to be a great way to control your watch. the Slider and picker controls do use it, but direct developer use was prohibited in Watch OS2. In a nice change, Watch OS3 does. In this lesson, we’ll show how to get data from the digital crown in your applications.
Start a new Xcode WatchOS project DigitalCrownDemo. Deselect the notifications and save the project.
Add three labels to the watch interface. Title them as follows:
In the assistant editor, Connect the labels to outlets like this:
@IBOutlet var statusLabel: WKInterfaceLabel! @IBOutlet var valueLabel: WKInterfaceLabel! @IBOutlet var rpsLabel: WKInterfaceLabel!
We’ll track two data values the rotation speed and a value based on the change in value from the watch. Add them to the controller
var rps = 0.0 var value = 0.0
Add a update method for the display:
func updateLabels(){ valueLabel.setText(String(format:"Value:%1.3f",value)) rpsLabel.setText(String(format:"RPS:%1.3f rps",rps)) }
The crown uses a delegate. In interface controller, Adopt the WKCrownDelegate
class InterfaceController: WKInterfaceController,WKCrownDelegate {
As for any delegate, set the delegate
to self
, which in this case is on the crownSequencer
. Do this in awake(withContext)
override func awake(withContext context: AnyObject?) { super.awake(withContext: context) crownSequencer.delegate = self }
Several objects, including pickers, scrolling views and tables can have the focus of the crown. For the crownSequencer
to work as an input in your code, must set the focus to it. Set the focus for the crown as late as possible to make sure it gets control. After the interface appears is best, so I set it in didAppear()
override func didAppear() { crownSequencer.focus() }
Add the crownDidRotate
delegate method, which returns values when there is a change in the rotation of the crown.
//MARK: Delegates func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) { value += rotationalDelta rps = (crownSequencer?.rotationsPerSecond)! statusLabel.setText("Moving") updateLabels() }
We have two arguments in this delegate method: the crownSequencer and the rotationalDelta. The Rotational debts is the change between the current and last position of the crown’s rotation. One of the properties of the crownSequencer is rotations per second, giving a speed to the rotations. The code places the rotational delta and the rotations per send into a string for output to the watch. Most likely we’ll want a position from the rotational delta, so we’ve added it to value.
Add the crownDidbecomeIdle
delegate method
func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) { rps = (crownSequencer?.rotationsPerSecond)! statusLabel.setText("Stopped") updateLabels() }
This delegate method fires when the crown stops. The code prints that the crown stopped and the last rotational speed before it did.
Run the code on the 42mm simulator. To use the crown hardware in the simulator, After a single click on the background of the watch face drag up and down two fingers. Stopping and starting the drag, you will see the display change.
The crownSequencer
is smart enough to know your watches orientation from your settings. Up is always a positive value and down is always negative. there’s a lot to do with this, from making new controls for your watch to an input for small games with sprite kit and scene kit.
The Whole Code
// // InterfaceController.swift // DigitalCrownDemo WatchKit Extension // // Created by Steven Lipton on 8/22/16. // Modified 6/22/18 WatchOS5 // Copyright © 2016 Steven Lipton. All rights reserved. // import WatchKit import Foundation class InterfaceController: WKInterfaceController, WKCrownDelegate { @IBOutlet var statusLabel: WKInterfaceLabel! @IBOutlet var valueLabel: WKInterfaceLabel! @IBOutlet var rpsLabel: WKInterfaceLabel! var value = 0.0 var rps = 0.0 func updateLabels(){ valueLabel.setText(String(format:"Value:%1.3f",value)) rpsLabel.setText(String(format:"RPS:%1.3f rps",rps)) } override func awake(withContext context: Any?) { super.awake(withContext: context) crownSequencer.delegate = self } override func didAppear() { crownSequencer.focus() } //MARK: Delegates func crownDidRotate(_ crownSequencer: WKCrownSequencer?, rotationalDelta: Double) { value += rotationalDelta rps = (crownSequencer?.rotationsPerSecond)! statusLabel.setText("Moving") updateLabels() } func crownDidBecomeIdle(_ crownSequencer: WKCrownSequencer?) { rps = (crownSequencer?.rotationsPerSecond)! statusLabel.setText("Stopped") updateLabels() } }
Leave a Reply