Training and Instructional Design
Posted on May 29, 2015 by Steven Lipton
In our lessons setting up navigation controllers on the Apple Watch, we’ve learned how to set up the storyboard, how to use push controllers programmatically and introduced sending data to another controller and back using contexts and delegates. In this lesson, we’ll pass data when you have a segue and learn how to dismiss controllers.
We’ll be using the same storyboard from the previous lesson and expanding it to match the illustration above. Open the project and go to the storyboard for the watch . Bring the Down Branch into view. If you did the exercise in part 2, two child controllers, One and Two should also be visible.
If not, add two interfaces. In the attributes inspector, make the identifier and title One, and the identifier and label for the other Two. Control-drag from the button One to the interface One. Select a push segue. Control-drag from the Two Button to the Two interface. Select a push segue. It should now match the screenshot above.
Add a slider to the down branch interface. Set the slider’s position to left and bottom.
In the attributes inspector, change the attributes of the slider to make a slider from 0 to 10 with an initial value of 1:
On the One interface, add a group to the interface. Like the last lesson, we will use this as a fake background to change colors. Set the width and height to Relative to Container. In the last lesson, we had a rounded corners on the background. We can control how rounded a corner we want with the radius attribute. Check on Custom and set to 0 in the attribute inspector to give us sharp corners.
In the color, change the color to Light Gray. Change the group to a vertical group:
Add a label and a button to the group. Position the label Top and Left, and position the button Bottom and Left. Change the label’s text to Dark Text Color, with the text View One. Center align the label. Change the button’s color to Dark Text Color. Change the text to Back.
In interface Two, add a label and two buttons. Position the label Top and Center. Position one button Left and Center and the other button Bottom and Center. Change the text on the label to View Two. Change the title on the center button to Back and the bottom button to Root.
Make some controllers for these three interfaces. Press Command-N and Make a new Cocoa Touch WKInterface subclass called DownInterfaceController.
In the file dialog box toward the bottom you will find the save to group.
Make sure you send it to Watchkit Extension Group, since it defaults to the WatchKit app. If you don’t, your app will crash and Xcode may have some problems.
Do the same for two more controllers OneInterfaceController and TwoInterfaceController.
Go back to the storyboard. Using the identity inspector, assign the proper classes to the Down branch, One and Two interface.
Open the assistant editor. Select the slider on the Down Branch interface. Control-drag to the code and make an action sliderDidChange.
Select the label on the One interface. Control drag and make an outlet label. Select the group. Add an outlet named backgroundGroup. Select the button. Control drag from the button and make an action backPressed. you should have in the code the following:
@IBOutlet weak var label: WKInterfaceLabel! @IBOutlet weak var backgroundGroup: WKInterfaceGroup! @IBAction func backPressed() { }
Select the label on the Two Interface. Control-drag and make an outlet label. Control-drag from the Back button and make an action backPressed. Control drag from the Root button and make an action rootPressed. You will have this in your code:
@IBOutlet weak var label: WKInterfaceLabel! @IBAction func backPressed() { } @IBAction func rootPressed() { }
Close the assistant editor. Go to the DownInterfaceController
code. Add the following variable:
var sliderValue:Float = 1.0
Change the action sliderDidChange
to this:
@IBAction func sliderDidChange(value: Float) { sliderValue = value }
When we used pushInterfaceController
we had the context as a parameter. For a segue, there is an equivalent to prepareForSegue
in WatchKit, though it works differently. The method contextForSegueWithIdentifier
returns an object that will be used as the context. Like prepareForSegue
, use the identifier for a set of conditionals to send the correct context to a destination controller. As an example, add the following code:
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? { if segueIdentifier == "One"{ return sliderValue } if segueIdentifier == "Two"{ return String(format: "Two %f",sliderValue) } return nil }
We have two segues, One and Two. I use a different context for each. If I go to One, I send the Float
value of sliderValue
. If I go to Two, I send a string. If I get a value that is neither of these, I return nil
. If anything goes wrong here, such as forgetting to set the identifiers correctly, I’ll get a unexpectedly found nil while unwrapping an Optional value
error when I unwrap context
in the destination. That will tell us to check here first for an error.
Speaking of identifiers, If we are using this method, just like prepareForSegue,
our segues need identifiers. We haven’t added them yet, so select the segue for One and make the identifier One.
Add the identifier Two to the Two segue.
Convert the context to properties in the destination controllers. Go to the OneInterfaceController
code. Add the following code to awakeWithContext:
override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. var value = context as! Float label.setText(String(format:"One: %f",value)) var hue = CGFloat(value / 10.0) backgroundGroup.setBackgroundColor(UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)) }
In this controller, we had a float
value, which we make a variable value
to store its value. We format value
in the label, then use value
to create a hue that we use for a background color.
Now go to the TwoInterfaceController
and change awakeWithContext
to this:
override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) let labelString = context as! String label.setText(labelString) // Configure interface objects here. }
This code is simpler than the last code. It takes the string and outputs to the label.
Navigation dismissal is even easier than UIKit. There is no animation flag to get in the way. Just pop the controller with popcontroller.
Go to OneInterfaceController
and change backPressed
to this:
@IBAction func backPressed() { popController() }
Then go to TwoInterfaceController
and change backPressed
and rootPressed
to this:
@IBAction func backPressed() { popController() } @IBAction func rootPressed() { popToRootController() }
We have the method popToRootController
available to us to pop all the way back to the root.
Build and Run. Tap the Down button
Tap One.
Tap Back, and you go back to Down Branch. Click on the slider to change its value,
then tap One. We get a color and value change.
Tab Back again, and then tap Two. We get the second screen with a value.
Tap Root and we are back at the root controller.
Here’s a video of the demo in operation:
We have covered most of what is necessary to use navigation and page controllers in WatchKit. you can do alomst every you can do with a UIKit navigation controller with a WatchKit interface controller as a hierarchical controller. Storyboard segues, passing data between controllers with segues, contexts and delegates and dismissing interfaces is pretty clear. While we learned you cannot add both page and hierarchical interfaces to the same app, in our next lesson we’ll learn another controller you can use in both types of interfaces: modal interfaces. Along the way, we’ll see how to use contextForSegueWithIndentifier
for page interfaces.
// // DownInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/28/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation class DownInterfaceController: WKInterfaceController { var sliderValue:Float = 1.0 @IBAction func sliderDidChange(value: Float) { sliderValue = value } override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? { if segueIdentifier == "One"{ return sliderValue } if segueIdentifier == "Two"{ return String(format: "Two %2.2f",sliderValue) } return nil } override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
// // OneInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/28/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation class OneInterfaceController: WKInterfaceController { @IBOutlet weak var label: WKInterfaceLabel! @IBOutlet weak var backgroundGroup: WKInterfaceGroup! @IBAction func backPressed() { popController() } override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. var value = context as! Float label.setText(String(format:"One: %f",value)) var hue = CGFloat(value / 10.0) backgroundGroup.setBackgroundColor(UIColor(hue: hue, saturation: 1.0, brightness: 1.0, alpha: 1.0)) } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
// // TwoInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/28/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation class TwoInterfaceController: WKInterfaceController { @IBOutlet weak var label: WKInterfaceLabel! @IBAction func backPressed() { popController() } @IBAction func rootPressed() { popToRootController() } override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) let labelString = context as! String label.setText(labelString) // Configure interface objects here. } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
// // UpInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/21/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation class DarkOrLight:NSObject{ var isDarkColor = false var count = 0 var delegate:AnyObject? = nil } class UpInterfaceController: WKInterfaceController,ColorInterfaceDelegate { //MARK: - Outlets var isGoingToGray = true var isDarkColor = true @IBOutlet weak var colorSwitch: WKInterfaceSwitch! @IBOutlet weak var navigationSwitch: WKInterfaceSwitch! //MARK: - Actions @IBAction func navigationSwitchDidChange(value: Bool) { if value{ navigationSwitch.setTitle("Gray") }else{ navigationSwitch.setTitle("Color") } isGoingToGray = value } func colorDidChange(value:Bool) { isDarkColor = value print(isDarkColor) updateColorSwitch() //popController() } func updateColorSwitch(){ colorSwitch.setOn(isDarkColor) if isDarkColor{ colorSwitch.setTitle("Dark") }else{ colorSwitch.setTitle("Light") } } @IBAction func colorSwitchDidChange(value: Bool) { isDarkColor = value updateColorSwitch() } @IBAction func goButtonPressed() { //prepare context var myContext:Bool? = isDarkColor //logical navigtion if isGoingToGray { var myContext:Bool? = isDarkColor pushControllerWithName("Gray", context: myContext) }else{ var myContext = DarkOrLight() myContext.isDarkColor = isDarkColor myContext.count = 2 myContext.delegate = self // <-- add the delegate pushControllerWithName("Color", context: myContext) } } //MARK: - Life Cycle override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() updateColorSwitch() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
// // GrayInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/21/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation class GrayInterfaceController: WKInterfaceController { //MARK: - Properties, Constants and Outlets var isDarkColor = false let darkColor = UIColor.darkGrayColor() let lightColor = UIColor.lightGrayColor() @IBOutlet weak var grayBackgroundGroup: WKInterfaceGroup! //MARK: - Life Cycle override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. isDarkColor = context as! Bool //change context into property if isDarkColor { grayBackgroundGroup.setBackgroundColor(darkColor) } else { grayBackgroundGroup.setBackgroundColor(lightColor) } } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
// // ColorInterfaceController.swift // SwiftNavigationDemo // // Created by Steven Lipton on 5/21/15. // Copyright (c) 2015 MakeAppPie.Com. All rights reserved. // import WatchKit import Foundation protocol ColorInterfaceDelegate{ func colorDidChange(value:Bool) } class ColorInterfaceController: WKInterfaceController { var isDarkColor = false let darkColor = UIColor(red: 68.0/255.0, green: 0.0, blue: 136.0/255.0, alpha: 1.0) let lightColor = UIColor(red: 187.0/255.0, green: 1.0, blue: 0, alpha: 1.0) var delegate:ColorInterfaceDelegate? = nil @IBOutlet weak var colorSwitch: WKInterfaceSwitch! @IBOutlet weak var colorBackgroundGroup: WKInterfaceGroup! @IBAction func colorSwitchDidChange(value: Bool) { isDarkColor = value refreshDisplay() delegate?.colorDidChange(value) } func refreshDisplay(){ colorSwitch.setOn(isDarkColor) if isDarkColor { colorBackgroundGroup.setBackgroundColor(darkColor) colorSwitch.setTitle("Dark") } else { colorBackgroundGroup.setBackgroundColor(lightColor) colorSwitch.setTitle("Light") } } override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. //convert the context to usuable properties let myContext = context as! DarkOrLight delegate = myContext.delegate as? ColorInterfaceDelegate //<-- add delegate let count = myContext.count isDarkColor = myContext.isDarkColor as Bool // update display refreshDisplay() } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } }
Category: GUI, ios8, Swift Programming, Tutorial, Uncategorized, WatchKitTags: Apple Watch, awakeWithContext, button, contextForsegueWithIdentifier, popController, popToRootController, popViewControllerAnimated, Programming Apple watch, pushControllerWithName, segue, Story Board, storyboard, swift, UIviewcontroller, view controllers, Watchkit, WKInterfaceController
This site uses Akismet to reduce spam. Learn how your comment data is processed.
If you are making your own stuff, doing your own work and trying to sell it in the market, most advice isn't enough. You want it to work. You want to sell your works to those who most are touched by them. This newsletter is for you. I am one of those people like you, creative, independent and maybe a little bit crazy to the outside world. I'll talk about writing good apps for Apple platforms, the tools and API's to get there. Along the way I will show you how to become you best creative self. I will show you how to build and create, and make some money at it too.
Get exclusive content, new post notifications, tips and more in A Slice of App Pie.
This site may include links that provide monetary compensation to the owners of this site.
Pingback: Swift WatchKit: Introducing Navigation to the Apple Watch(Part 3: Using Delegates and Contexts) | Making App Pie