Changes

Jump to: navigation, search

A watchOS 2 WatchKit Page-based Interface Tutorial

18,648 bytes added, 17:32, 24 September 2015
New page: <table border="0" cellspacing="0" width="100%"> <tr> <td width="20%">Previous<td align="center">[[watchOS 2 App Dev...
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[watchOS 2 Page-based User Interfaces and Modal Interface Controllers|Previous]]<td align="center">[[watchOS 2 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[Handling User Input in a watchOS 2 WatchKit App|Next]]</td>
<tr>
<td width="20%">watchOS 2 Page-based User Interfaces and Modal Interface Controllers<td align="center"><td width="20%" align="right">Handling User Input in a watchOS 2 WatchKit App</td>
</table>
<hr>


<google>BUY_WATCHOS2</google>


This chapter will work through the creation of an example that makes use of both page-based navigation and modal interface controller presentation within a WatchKit app.

The project created in this chapter will involve a variation on the fitness app created in the chapter entitled A WatchKit Table Tutorial. In this case, the user interface will consist of a sequence of scenes within a page-based interface, each containing a fitness exercise image and a button. When selected by the user, the button will cause a modal interface controller to appear containing a Timer object which will show the user how much time is left to perform the corresponding workout step. When the countdown reaches zero, the app extension will use the taptic engine within the Apple Watch device to physically notify the user via haptic feedback that the time has elapsed.

== Creating the Page Example Project ==

Start Xcode and create a new iOS project. On the template screen choose the Application option located under watchOS in the left hand panel and select iOS App with WatchKit App. Click Next, set the product name to PageDemoApp, enter your organization identifier and make sure that the Devices menu is set to Universal. Before clicking Next, change the Language menu to Swift and make sure that the Include options are all switched off. On the final screen, choose a location in which to store the project files and click on Create to proceed to the main Xcode project window.

== Adding the Image Files to the Project ==

Before designing the interface controller scene, some image files need to be added to the project. These are contained within the fitness_icons_large folder of the sample code download which may be obtained from the following URL:


[http://www.ebookfrenzy.com/code/watchOS2BookSamples.zip http://www.ebookfrenzy.com/code/watchOS2BookSamples.zip]


Within the Project Navigator panel, select the Assets.xcassets entry listed under the PageDemoApp WatchKit App folder so that the asset catalog panel appears. Ctrl-click in the left hand panel of the asset catalog and select the Import… option from the resulting menu.

In the file import selection panel, navigate to and select the fitness_icons_large folder before clicking on the Open button. The images will be imported into the asset catalog as an image set named fitness_icons_large.

== Designing the First Interface Controller Scene ==

The user interface for the app is going to consist of three scenes, each containing an Image object and a Button. Select the Interface.storyboard file located under PageDemoApp WatchKit App in the Project Navigator panel, locate the main scene in the Interface Builder tool and add an Image and a Button object to the scene so that it appears as shown in Figure 10-1:


[[Image:watchkit_page_main_scene.png]]

Figure 10-1


Select the Image object in the scene, display the Attributes Inspector and select the walking image from the Image menu. With the image object still selected and remaining in the Attributes Inspector, change the Mode setting to Aspect Fit and the Horizontal alignment attribute to Center.

Double click on the Button object and change the text so it reads “Start”. Keep the Button object selected and change the Vertical Alignment property in the Attributes Inspector to Bottom. On completion of these settings the layout should match that shown in Figure 10-2:

[[Image:watchkit_page_first_scene_ui.png]]

Figure 10-2


== Adding More Interface Controllers ==

This phase of the project requires that an additional two interface controllers be added to the storyboard file. Locate the Interface Controller item in the Object Library panel and drag and drop two instances onto the storyboard canvas so that the objects are positioned to the right of the main controller (Figure 10-3):


[[Image:watchkit_page_three_controllers.png]]

Figure 10-3


Since the user interface design for the remaining two scenes will be similar to that of the first, a quicker option than manually designing the scenes is to cut and paste the objects from the first scene. Hold down the Shift key and click on the Image and Button objects in the main scene. Press Cmd-C to copy the objects, select the second interface controller scene and use Cmd-V to paste the objects into the scene. Select the third scene and perform the paste operation once again.

Select the Image object in the second scene, display the Attributes Inspector and change the image value to the treadmill image. Repeat this step to change the image in the third scene to weights. On completion of these steps, the three interface controller scenes should appear as shown in Figure 10-4:


[[Image:watchkit_page_scenes_designed.png]]

Figure 10-4


== Establishing the Segues ==

For the three interface controllers to work within a page-based navigation interface, the controllers must be connected using next page segues. To achieve this, Ctrl-click on the title bar of the first scene (the title bar is the area which currently shows the time). Drag the resulting line to the second interface controller until it highlights (Figure 10 5) and release the line. In the segue relationship menu select the next page option.


[[Image:watch_kit_next_page_segue_line.png]]

Figure 10-5


Repeat the above steps to establish a segue between the second and third interface controllers so that the storyboard matches that of Figure 10-6:


[[watchkit_page_segues_added.png]]

Figure 10-6


Run the app and test that the three scenes can now be navigated by making swiping motions on the display:


[[Image:watchkit_example_page_running.png]]

Figure 10-7


== Assigning Interface Controllers ==

The scene now consists of three scenes but at the moment only the first scene has an Interface Controller associated with it. One approach to take at this point might be to add two new interface controller classes to the project, one for each of the two remaining scenes in the page-based navigation set. Since all three scenes essentially perform the same task, however, a more efficient approach is to use the same interface controller class for all three. The first scene is already assigned to the InterfaceController class, so select the second scene so that it is highlighted in blue, display the Identity Inspector panel and change the Class menu setting to InterfaceController (Figure 10-8). Repeat these steps with the third interface controller selected.


[[Image:watchkit_page_setting_interface_controller_class.png]]

Figure 10-8


In each case, make sure that the Module menu is set to PageDemoApp_WatchKit_Extension.

== Adding the Timer Interface Controller ==

When the Start button is tapped on any of the paged controller scenes an additional scene will be modally displayed to the user. This scene will contain a single object in the form of a WKInterfaceTimer instance. Start by dragging and dropping an Interface Controller object from the Object Library panel so that it is positioned beneath the three existing scenes in the storyboard:


[[Image:watchkit_page_modal_scene_added.png]]

Figure 10-9


Drag and drop a Timer object from the Object Library onto the newly added scene. With the Timer object selected, display the Attributes Inspector panel and set the Horizontal and Vertical alignment attributes to Center.
Click on the “T” button within the Font text field, select System from the popup panel, Bold from the Style menu and set the Size setting to 28 as shown in Figure 10-10:


[[Image:watchkit_timer_set_font.png]]

Figure 10-10


On completion of these steps the scene should match that illustrated in Figure 10-11:


[[Image:watchkit_timer_scene_ui.png]]

Figure 10-11

<google>BUY_WATCHOS2</google>
By default, the Timer object displays hours, minutes and seconds as numbers (referred to as positional format). A range of other configuration alternatives is available within the Attributes Inspector for changing both what is displayed on the timer and how it is displayed. It is, for example, possible to also display month, week, day and year values. It is also possible to change the way in which this information is displayed, including fully spelling out the time and date in words instead of numbers. Although this example will use the default setting, it is worth reviewing the options in the Attributes Inspector panel for future reference.
Locate and select the PageDemoApp WatchKit Extension entry in the Xcode Project Navigator panel, Ctrl-click on it and select the New File… menu option. Create a new watchOS WatchKit Class file named TimerInterfaceController subclassed from WKInterfaceController and proceed with the steps to generate the class source file into the PageDemoApp WatchKit Extension project folder.

Return to the Interface.storyboard file, select the timer scene so that it is highlighted in blue and, using the Identity Inspector panel, change the Class menu to TimerInterfaceController. Switch to the Attributes Inspector panel and enter TimerInterfaceController into the Identifier field.

Select the Timer object in the timer scene, display the Assistant Editor panel and verify that it is displaying the source code for the TimerInterfaceController.swift file. Ctrl-click on the Timer object and drag the resulting line to a position immediately after the class declaration line in the Assistant Editor panel. Release the line and establish an outlet named workoutTimer. On completion of these steps the start of the TimerInterfaceController.swift file should read as follows:

<pre>
import WatchKit
import Foundation

class TimerInterfaceController: WKInterfaceController {

@IBOutlet weak var workoutTimer: WKInterfaceTimer!

override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

// Configure interface objects here.
}
.
.
.
}
</pre>

With the timer interface controller added and configured, the next step is to implement the modal segues so that the interface controller is presented when the Start buttons are tapped.

== Adding the Modal Segues ==

When the buttons on the three paged scenes are tapped by the user, the timer scene will need to be displayed modally. Establish the first segue by Ctrl-clicking on the Button object in the first scene and dragging to the timer scene. Release the line and select the modal option from the Action Segue menu. Repeat these steps for the Button objects on the remaining two scenes so that the storyboard resembles Figure 10-12:


[[Image:watchkit_pages_modal_segues_set.png]]

Figure 10-12


Compile and run the app and verify that tapping the button on each of the scenes causes the scene for the timer interface controller to appear. Note that a Cancel option is provided on the timer scene to return to the original scene.

== Configuring the Context Data ==

When the timer interface controller is presented it will need to be initialized with a countdown time. The amount of time will vary depending on the scene from which the timer was launched. This means that we will need to find a way to identify which of the three modal segues triggered the appearance of the timer interface controller and, based on that information, pass the appropriate time duration through as context data.

The first step in this process is to establish a way to distinguish one segue from another by assigning identifiers. Begin by selecting the segue line from the left most scene to the timer scene and displaying the Attributes Inspector panel. Within the panel, enter walkSegue into the Identifier field as shown in Figure 10-13:


[[Image:watchkit_page_demo_segue_id.png]]

Figure 10-13


Follow the same steps to assign identifiers to the two remaining modal segues named runSegue and weightsSegue respectively. To avoid encountering a known problem with Xcode whereby the identifiers are not saved, be sure to save the storyboard after entering identifier (Cmd-S).

As explained in the chapter entitled [[watchOS 2 Page-based User Interfaces and Modal Interface Controllers]], context data can be passed during a modal segue transition by implementing the contextForSegueWithIdentifier method within the originating interface controller. Passed as a parameter to this method is the identifier of the segue that triggered the call, allowing us to configure the context data according to the segue.
Select the InterfaceController.swift file and implement the contextForSegueWithIdentifier method so that it reads as follows:

<pre>
override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {

var contextValue: NSTimeInterval?

switch (segueIdentifier) {
case "walkSegue":
contextValue = 10
case "runSegue":
contextValue = 20
case "weightsSegue":
contextValue = 30
default:
break
}
return(contextValue)
}
</pre>

The code in the above method declares a variable in which to store the countdown time before using a switch construct to identify the current segue and select a time value which is then returned. The duration value will then be passed by the WatchKit framework to the destination interface controller via the awakeWithContext method of that class. For testing purposes, the duration values are set to seconds rather than minutes.

== Configuring the Timer ==

When the timer interface controller is now displayed it will be passed a time duration value via the awakeWithContext lifecycle method. This time duration will subsequently need to be assigned to the Timer object in the scene and the countdown started. The WatchKit Timer object is initialized by passing through an NSDate object configured to the current time, a time in the future or a time in the past. Passing through the current time and date will cause the timer to begin counting upwards from 0:00. Passing through a date and time in the future will cause the timer to begin counting down towards that time and date. Specifying a date and time in the past, on the other hand, will start the timer at that date and time and begin counting upwards from that point.

Select the TimerInterfaceController.swift file, locate the awakeWithContext method and modify it so that it reads as follows:

<pre>
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

if let duration: AnyObject = context {
let date = NSDate(timeIntervalSinceNow:
duration as! NSTimeInterval)
workoutTimer.setDate(date)
workoutTimer.start()
}
}
</pre>

The code verifies that a time value was passed through as the context object and uses that value to create an NSDate object configured to a time point in the future. The configured NSDate object is then used to initialize the Timer object using the previously configured outlet before the countdown is started.

Run the application and note that the timer is now initialized and begins counting down when the timer scene is presented. The time duration should also differ depending on which scene triggers the segue as defined in the earlier switch statement:


[[Image:watchkit_page_demo_timer_working.png]]

Figure 10-14


== Playing the Haptic Effect ==

The final feature to be added to the project is to set up to notify the user that the timer has reached zero. The problem that arises in implementing this behavior is that the WatchKit Timer object does not trigger an action when it reaches zero. The solution is to initialize an NSTimer instance to run for the same amount of time as the WatchKit Timer object and configured to call a selector method when the time has elapsed. The code to set up the NSTimer object needs to be added to the awakeWithContext method as follows:

<pre>
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

if let duration: AnyObject = context {

NSTimer.scheduledTimerWithTimeInterval(
duration as! NSTimeInterval,
target: self,
selector: Selector("playAlert"),
userInfo: nil,
repeats: false)

let date = NSDate(timeIntervalSinceNow:
duration as NSTimeInterval)
workoutTimer.setDate(date)
workoutTimer.start()
}
}
</pre>

The NSTimer object is configured to call a selector method named playAlert when the designated time has elapsed. The code in this method simply needs to obtain a reference to the current WKInterfaceDevice object and call the playHaptic method on that object, passing through a request for the Notification style of alert.

Edit the TimerInterfaceController.swift file and implement the playAlert method as follows:

<pre>
func playAlert() {
let device = WKInterfaceDevice.currentDevice()
device.playHaptic(.Notification)
}
</pre>

With the haptic feedback added to the project, compile and run the app on an Apple Watch device and test that the watch triggers a haptic “nudge” and plays the notification sound when the countdown reaches zero in the timer interface controller scene.

== Summary ==

This chapter has worked through the design and implementation of an example WatchKit app containing a page-based scene navigation interface. The example also highlighted the steps involved in implementing modal interface controller presentation and the passing of context during segue transitions. The chapter also introduced the WatchKit Timer object and explored the use of an NSTimer instance in parallel with a Timer object to receive notification of the timer reaching zero.


<google>BUY_WATCHOS2</google>


<hr>
<table border="0" cellspacing="0" width="100%">
<tr>
<td width="20%">[[watchOS 2 Page-based User Interfaces and Modal Interface Controllers|Previous]]<td align="center">[[watchOS 2 App Development Essentials|Table of Contents]]<td width="20%" align="right">[[Handling User Input in a watchOS 2 WatchKit App|Next]]</td>
<tr>
<td width="20%">watchOS 2 Page-based User Interfaces and Modal Interface Controllers<td align="center"><td width="20%" align="right">Handling User Input in a watchOS 2 WatchKit App</td>
</table>

Navigation menu