Getting user input
So far, we have had very little input from the user. She has launched the app and pressed a button, but the Watch is good for more than just presenting information. While the size of the screen makes some forms of input impractical, and text input via a keyboard must be top of that list, there are still many user input methods, both old and new at our disposal.
WatchKit's text input controller is a very simple way to gather text input (as the name might suggest) using the presentTextInputControllerWithSuggestions
method provided by WKInterfaceController
. When making this method call, you provide a list of text options from which the user may make a selection (she may also cancel, or choose voice input).
Firstly, we want to modify changeBorderColor()
to accept a String
argument which will tell it what color the user has selected. Replace the function as it stands with the following:
func changeBorderColor(colorString: String) { let newColor: UIColor switch colorString { case kRed: newColor = UIColor.redColor() case kPurple: newColor = UIColor.purpleColor() case kBlue: newColor = UIColor.blueColor() default: return } animateWithDuration(2.5, animations: { self.borderGroup.setBackgroundColor(newColor) }) }
The compiler will complain that it knows nothing of kRed
, KPurple
, and kBlue
. These are constants we will create to prevent any typos creeping into our code, such as red
instead of Red
. Add these constant declarations directly after the import
statements at the top of the code:
import WatchKit import Foundation let kRed = "Red" let kPurple = "Purple" let kBlue = "Blue"
The compiler warnings will now disappear.
Next, remove the code inside buttonTapped()
, replacing it with a call to a new method presentColorOptionsToUser()
and add the definition for the new method:
func presentColorOptionsToUser() { presentTextInputControllerWithSuggestions( [kRed, kPurple, kBlue], //1 allowedInputMode: WKTextInputMode.Plain, //2 completion:{(results: [AnyObject]?) -> Void in //3 if let validResults = results, //4 let resultString = validResults[0] as? String //5 { self.helloButton.setTitle(resultString) self.changeBorderColor(resultString) } }) }
This is quite an intense chunk of code, so let us take a detailed look at what we are doing.
- We provide an
Array
of constantString
values that are to be offered to the user. allowedInputMode
is set to.Plain
, since we have no use for emojis in this particular context (!).- We specify the
completion
closure, which will be called when the user makes a selection (or cancels). This closure takes an optional array of objects, which we have calledresults
; this array will contain the selectedString
, ornil
if noString
was selected. - We check that
results
is not nil. - We check that first object in the
validResults
array is indeed aString
object and use that the String to set the title ofthe helloButton
, and as the argument tochangeBorderColor(colorString: String)
.
Check that your complete code, including the constant declarations, looks like this:
import WatchKit import Foundation let kRed = "Red" let kPurple = "Purple" let kBlue = "Blue" class InterfaceController: WKInterfaceController { @IBOutlet var helloButton: WKInterfaceButton! @IBOutlet var borderGroup: WKInterfaceGroup! override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) } @IBAction func helloButtonTapped(){ presentColorOptionsToUser() } func presentColorOptionsToUser() { presentTextInputControllerWithSuggestions( [kRed, kPurple, kBlue], allowedInputMode: WKTextInputMode.Plain, completion:{(results: [AnyObject]?) -> Void in if let validResults = results, let resultString = validResults[0] as? String { self.helloButton.setTitle(resultString) self.changeBorderColor(resultString) } }) } func changeBorderColor(colorString: String) { let newColor: UIColor switch colorString { case kRed: newColor = UIColor.redColor() case kPurple: newColor = UIColor.purpleColor() case kBlue: newColor = UIColor.blueColor() default: return } animateWithDuration(2.5, animations: { self.borderGroup.setBackgroundColor(newColor) }) } }
When you run the app, tapping the helloButton
will now bring up a modal screen offering you the options seen in the screenshot below:
The Cancel button is created automatically by WatchKit. Tapping it will dismiss the modal view, and also return nil
to the closure that we provided when calling presentTextInputControllerWithSuggestions
, and so our results array will be nil
. Our code checks for nil
, and thus returns without doing anything.
Tapping the microphone icon will also do nothing; it will not even dismiss the modal view, since Watch Simulator does not handle voice input.
As we can see, the list of text options simply mirrors the array you passed as first argument into presentTextInputControllerWithSuggestions
. Tapping one of these will dismiss the modal view and return the appropriate String
to the closure, and thus we can use the results
value to extract that String
.
Did you really read all that before tapping one of the options? Either way, on tapping a color, you will see our button's apparent border (really a Group object) animate to its new color and change its title to reflect the selected text.
A subtle but pleasing animation, I am sure you will agree.