Accessing the Digital Crown in WatchOS

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.

2016-08-22_06-26-21

Add three labels to the watch interface. Title them as follows:

2016-08-22_05-51-19

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 <code>WKCrownDelegate</code>
class InterfaceController: WKInterfaceController,WKCrownDelegate {

The crown must have focus to return information. In awake(withContext) set the focus for the crown

    
override func awake(withContext context: AnyObject?) {
    super.awake(withContext: context)
    crownSequencer.focus()
    crownSequencer.delegate = self
}

The interface controller has a property crownSequencer that monitors the activity on the crown. We set the focus to crownSequencer and set the delegate method locations to this class.

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.
//  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.focus()
        crownSequencer.delegate = self
    }
    //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()

    }

    
}

4 Replies to “Accessing the Digital Crown in WatchOS”

  1. Have you tried putting code like this into a push or modal segue? Every time I run code with this form and go back to the root Interface controller the digital crown stops working completely.

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