Tag Archives: button

Where is Update Frames in Xcode 8.1?

A tech author’s work is never done. As soon as he or she completes manuscript and gets it published, the manuscript almost immediately becomes obsolete. In my case, Practical Autolayout for Xcode 8 went obsolete  a day before I published, but I had no idea about a major change in Xcode 8.1.

Until Xcode 8.1, if you wanted to update a frame with new constraints, you had two possibilities. The first was in the pinpinMenuButton and align alignment iconmenu to update as you were setting the constraints.

2016-11-29_06-05-32

The second was a selection in the resolver resolver button 2016-10-01_13-27-48

It seems everyone, including me was not ready for a change Apple made in Xcode 8.1. If you go to look for Update Frames in the resolver resolver button, it is missing:

2016-11-29_06-11-52

So where did it go?

Apple moved this to an icon on the auto layout toolbar and deleted it from the menus.

2016-11-28_07-29-22

If it were me, I wouldn’t have deleted it from the menus in such an abrupt way. Apple did. This Update Frame button  has some different behaviors  from its predecessor on the menu, and I’d like to explain that using some examples from Chapter 3 of Practical Autolayout for Xcode 8

Set up a storyboard that looks something like this with a label Hello Pizza, a text view, and  three  buttons, Pepperoni, Cheese, and Done:

2016-11-29_05-54-14

Select the Hello Pizza label.  Click the pin buttonpinMenuButton in the auto layout toolbar. In the popup, set the top to 0 points, the left to 0 points and the left to 0 points.  Leave Update Frames as None

2016-11-29_05-56-06

Add the 3 constraints. The Hello Pizza Label will show misplacement constraints.

2016-11-29_05-56-32

Press the Update Frames button update frames  and the frame updates.

2016-11-29_06-37-41

This is not always the result. You must have all constraints satisfied before the button will update frames. For example, select the text view. Press the align button alignment iconand center the text view by checking on Horizontally in Container and Vertically in Container.

2016-11-29_05-54-38

Again don’t update frames, but click  Add 2 constraints. You’ll see an ambiguous constraint in red.

2016-11-29_05-55-13

If you click the update frames button nothing happens. Until a frame has no ambiguity(i.e. no red constraint errors), you cannot update it. Most often that is setting a size. For the text box, set an absolute size in the pin menu pinMenuButton  of 175 points in both directions.

2016-11-29_05-57-41

Add the constraints. The errors all turn to misplacements.

 2016-11-29_05-58-39

Once all misplacements, you can update the frame with update frames.

2016-11-29_07-15-35

Priorities are not assumed with the new update frames button. When there is an ambiguity in size between two frames that depend on each other for size, you must specify the a priority for them or set a size.  Take for example these two buttons.

2016-11-29_05-45-35

Pepperoni is pinned to the left margin, the label above it and the text view below it. Cheese is pinned 10 points from Pepperoni, aligned to the top of Pepperoni, and pinned 10 points from the right margin. We’d like to have two buttons that fill the available space.

The option used in Practical Auto Layout for these buttons is to make them the same size. Control drag from Pepperoni to Cheese. A menu appears.

2016-11-29_06-56-57

Shift select Equal Width and Equal Heights, then hit the Add Constraints selection. The ambiguity changes to misplacements.

2016-11-29_06-57-14

Select both the Pepperoni and Cheese buttons. Hit the Update Frame button update frames and two equally sized buttons appear

2016-11-29_06-58-00

The other, more advanced option is to change priority of one of the buttons so they are not equal. Both are by default 250.  Going back to the original ambiguous layout,

2016-11-29_05-45-35

changing the content hugging priority of Pepperoni from 250 to 251 tells auto layout for Pepperoni to keep its size and Cheese to stretch to make up the difference.

2016-11-29_06-56-19

Priorities are covered in detail in Chapter 12 of Practical Autolayout for Xcode 8.

I’ll be updating the book shortly. Until then or if you cannot update your book,  consider this an errata to the versions now available.

practical-autolayout-x8-newsletterPurchase the book for  Kindle and iTunes  here:

get_it_on_ibooks_badge_us_1114

Swift WatchKit: Introducing Navigation to the Apple Watch(Part 4: Dismissals and Segues)

2015-05-29_06-46-47

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.

2015-05-28_05-32-38

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.

2015-05-28_05-46-38

In the attributes inspector, change the attributes of the slider to make a slider from 0 to 10 with an initial value of 1:

2015-05-29_06-13-09

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.

2015-05-28_05-50-34

In the color, change the color to Light Gray.  Change the group to a vertical group:

2015-05-28_05-57-54

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.

2015-05-28_06-10-34

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.

2015-05-28_06-10-49

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.

2015-05-28_06-27-38

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.

2015-05-28_06-22-42

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.

2015-05-28_06-50-20

Add the identifier Two to the Two segue.

2015-05-28_06-50-57

 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.

Dismissal Code

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

2015-05-29_06-15-13

Tap One.

2015-05-29_06-15-43

Tap Back, and you go back to Down Branch.  Click on the slider to change its value,

2015-05-29_06-17-21

then tap One. We get a color and value change.

2015-05-29_06-16-38

 

Tab Back again, and then tap Two. We get the second screen with a value.

2015-05-29_06-17-09

Tap Root and we are back at the root controller.

2015-05-29_06-17-32

 

Here’s a video of the demo in operation:

 navigation watchkit demo

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.

The Whole Code

DownInterfaceController.swift

//
//  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

//
//  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

//
//  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

//
//  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

//
//  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

//
//  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()
    }

}

Swift WatchKit: Using Images on an Apple Watch (Part 1: Storyboard)

2015-05-05_09-58-16Goodbye Emoticons! Up to this point in our lessons for programming Apple Watch we’ve used emoticons for graphics. It’s time to introduce true images to our WatchKit apps.

There are two ways to use graphics. We’ll discuss adding images to Buttons, Groups, and Interface controller and the slider’s min and max icons.  We’ll also use WKInterfaceImage, the Watch Kit equivalent to UIImage. In this first part we’ll work with the storyboard, then in Part two, we’ll look at using images in code.

Images on the Watch

With such a small size, we need to be careful about image sizes. The 38mm  watch has a screen size of 272 px x 340 px  and the 42mm is  312 px x 390px. Both have an aspect ratio of 4:5.  Keep your images within this size for what you are going to do. With a very little amount of memory for storage, it’s also best to use one image that is easily scalable between the two sizes.  For a watch background, that would be the size you would need. Everything for a button, group or slider icon will be much smaller.

Apple’s documentation strongly recommends .png file formats for the graphics. All photos are retina so the filenames require the @2x  at the end of the file name For this lesson, we’ll use both photos and some designed elements. Here are our images:

We have two gradients, blueGradient@2x.png and greenGradient@2x.png:

blueGradient@2x       greenGradient@2x

A plus and minus icon for our sliders, minus sign@2x.png and plus sign@2x.png:

minus sign@2x       plus sign@2x

 

Two designed icons, runner@2x.png and walker@2x.png

runner@2x      walker@2x

 

And a photo pancakes@2x.png. I tend to make the photo slightly  larger than the space, and let the app re-size accordingly. We’ll see why shortly.

pancakes@2x

You can right-click each of these images and download individually. If you want all the images, you can download them in this zip file:WKImages

Make a New Project

Make a new a new single view application SwiftWatchImage by pressing Shift-Command-N in Xcode. Make  project using Swift as the language  and a universal app. Save the project. In the editor drop down select Add Target..

2015-05-05_06-25-20

and select the apple watch WatchKit App.

image

In the new target options, select on both notifications and glances. We will not be programming with them in this lesson, but they will give us practice space for images.

2015-05-05_06-27-50

You will get the activate warning. Go ahead and activate the new target.

In the navigator, open up the WatchKit Extension and the WatchKitApp folders. You’ll see an Images.xcassets in each folder. Each one stores the pictures in a different place. The extension lives on the phone, and stores the images on the phone. The app lives on the watch and stores the images on the watch.  Which one you use depends on memory conservation versus speed requirements.  You have more space on your phone, but they  send to the watch if they need to be loaded, which is slower. It’s faster to have an image on the watch, but you have very limited memory.  For our app we’ll place everything on the watch. Select the Images.xcassets in the Watchkit App group.

2015-05-05_06-35-33

In Finder, open the directory you stored the downloaded images. Select all the images.  Drag the images to the assets folder

2015-05-05_06-48-00

The files will load into the assets folder.

2015-05-05_06-53-57

For pre-loaded files, it is best to use the asset folder. The watch extension handles sending data to the watch more efficiently if you have images in  asset folders.

Using Backgrounds in the Storyboard

It also makes using photos easy, since they load into the media library.  Go to the WatchKit App Storyboard.  In the lower right where the object library is, click the film icon. You get the media library.

2015-05-05_06-56-40

To add an image to the device, drag the image to the device. Xcode will decide how to use the image.  Find the runner in the media Library. Drag the Runner  image into the lower group of the Glance.

2015-05-05_07-01-37

You find an image in the group.

2015-05-05_07-04-51

If you drag an image into a blank group, you will get a background.  Look at at the attributes inspector. You’ll see that the image is a background.

2015-05-05_07-05-08

You’ll also notice the image is slightly distorted.  There are several ways to fix this. For a background, we cannot change the size of the image.  Instead we change the View Mode, which currently is Scale to fill. Since the aspect ratio of the group does not match the aspect ratio of the image, the image distorts. Click the selector for mode. Select Aspect Fit.

2015-05-05_07-06-01

The aspect fit mode keeps the aspect ratio of the image, and fits the image completely into the view, leaving space if there is extra. The runner looks like this:

2015-05-05_07-07-44

You’ll notice that the graphic is fuzzy. I did this intentionally to point out that you need to think out your graphics before you load them. I made the runner and walker at 100px by 100px. It’s probably double that size in the image and the pixels are showing. You might get away with it in a glance, but it’s better to have a clear image. I made the pancakes at a higher resolution. Change to the pancakes, by clicking the Background image drop down, then selecting pancakes:

2015-05-05_07-12-22

The larger image looks better.

2015-05-05_07-12-52

Adding Images to the Button

Select the object library again. Drag a button from the  object library to the App storyboard. In the attribute Inspector, change the vertical position to Bottom.  Change the background to a GreenGradient and the Title to Run:

2015-05-05_07-39-13

You’ll get this:

2015-05-05_07-37-02

You’ll notice that the button gets larger. The gradient background is bigger that the default button size. The button height is set to Size to Fit Content. The best way to control the size is set the button to a proportion of the content size.  Change Height to Relative to Container.  Make the Adjustment 0.3 so we use about a third of the container for the button.

2015-05-05_07-43-10

The button changes  to a more appropriate size. Both sizes of watch will show a button a third the size of the screen.  Other methods would need changing for each watch size.

2015-05-05_07-46-09

Adding Icons to the Slider

You can customize your slider’s min and max icons with images. Add a slider into the storyboard by dragging it to the view.  We’ll use the default vertical position of top for the slider.

2015-05-05_08-16-19

In the attributes inspector, we can change the image for the max and min indicators.  Change the Min Image to the minus sign and the Max Image to the plus sign like this:

2015-05-05_08-18-40

The icons change. The 50px by 50px icons are above the largest size you really  want to use. Notice it makes the scale  of the slider smaller.

2015-05-05_08-19-59

Add a Group to the app storyboard.  Position it vertically in the center.  So we can see what we are doing, set the background to Light Gray.

2015-05-05_09-13-49

Drag an Image view from the object library  and place it in the group. Then place a label in the group.

2015-05-05_09-22-06

Position the image Horizontally Left and Vertically center. Leave the size the default Size to Fit Content.  Position the label Horizontally center and Vertically center.  We’ll do to the label the same as we did to the button, but for width. Set the width Relative to Container and make the value 0.66.

2015-05-05_09-25-54

In the label text type Walking, and Center Align it in the attributes inspector. For the image set the image to Walker.

2015-05-05_09-32-53

We could add a background to the user interface like this with the blue gradient:

2015-05-05_09-35-41

However the Human Interface Guide for Watch Kit has other ideas. Apple’s recommendation is to  use black for your background, with good reason:

Use black for your app’s background color. Black blends seamlessly with the Apple Watch bezel and creates the illusion of an edgeless screen.

Make your background the default color, which is black, and remove the background image.  Also make the Group Background color Default. It almost looks good, except that left curved corners does not match the square corners on the right of the image.

2015-05-05_09-42-08

The group is curving off the corners. Select the group. In the attribute inspector you will see an attribute Radius. This is the radius of the corner. Check Custom and change the Radius to 0.

2015-05-05_09-44-29

We have completed our app layout.

 2015-05-05_09-48-48

We have not coded anything yet. In our next lesson,  we’ll go into coding for images to make a full app.

Swift WatchKit Tutorials — A Basic Watch Kit App

Like a lot of people I ordered my Apple watch on April 10th, and now have to wait until June for it to arrive. I’m excited about the possibilities of the watch. I’m excited about writing applications for the watch, and I’m excited about sharing that information with you.

Introducing WatchKit through MVC

Understanding how to program for the Apple Watch is best described by a variation of the Model-View-Controller(MVC) pattern. It’s not exactly how the watch works, but I find it  a better mental framework for building apps than the real thing I’ve seen elsewhere.

MVC stands for Model-View-Controller. It’s a programming pattern to keep one organized. There is  a view which interacts with the user and a model that holds the data.  We can diagram it like this:

MVC normal app

The controller in the middle keeps the model and view from talking to each other. They have to go through the controller. If any change happens to the view,  then the controller responds by changing the view or the model. In the same way if there is a change in the data, the controller will change what is necessary in the view. This keeps things modular and allows for the use of different views without a lot of extra coding or modification. If you need more of a detailed explanation of MVC try reading this.

Basically, the Apple Watch is nothing more than a second view. It is not a computing device at all. It has its own mini-controller, but even that is on the phone in an extension.  We can diagram it like this:

MVC watchkit

The view controller of your app may talk to one of three types of view controllers (apps, glances and notifications) in the extension. The extension transmits just enough from your phone to the watch to make the view. The extension controller can respond to events from the watch or display things.  Compared to a scene on a storyboard, it is quite a limited number of things we can display or sense too.

WatchKit has seemingly many familiar objects to place on the display — until you start to work with them. In many ways, WatchKit is UIKit super-ultra-mega-lite. Most of what you know from iOS has been paired down to a bare minimum, and a delegate controller like UITableViewController now has only parameters instead of delegates and data sources.

Many attributes can only be set on the storyboard, and not programmatically.  There is several places where we have write-only objects, such a labels. You can not  get a label’s text, only set it.

In this series of  articles on WatchKit, I’m going to go through each object one by one. Today, we’ll do some introduction and then in future lessons explain everything.

A Sample project

I’m excited about the Apple watch because I’m a runner and I see it as a great fitness tool. I’ve also been eating too much pizza than is healthy while working on my book Swift Swift View Controllers so I need to get out there and lose some weight. We’ll be making a series of projects which will create a fitness app I’d like to use on my runs.  For the first app in our series, we’ll go through setup of a WatchKit app, and use labels and two new controls unique to watch kit  to make an timer app for me to run after I’ve had some pizza.

Start by creating a new app for your phone by Command-N and picking a single view template.  Name the project SwiftWatchPizzaRun using Swift  as the language. We won’t be writing anything for the phone, we just need it as a base.  I left the device type as Universal.  Save the project.

From the editor, select Editor> Add Target…

Screenshot 2015-04-12 11.58.10

Another window pops up asking which template to use for the template. Use the  WatchKit App Template found under Apple Watch  

image

You will get another window asking what configuration you want:

image

For this  app, uncheck  the Include Notification Scene. Press Finish.  You will get this message:

image

Activate the scheme.  You will be in the extension. Look at the Navigator and you will see some unfamiliar folders:

 image

We have one folder for the WatchKit extension, and one for the WatchKit app.  The controller is in the extension, and the storyboard with the views is in the WatchKit App.  You only have a view in a watch app.  Click on the storyboard and you will get a blank watch scene.

image

You will find in the object library some old friends from the iPhone and iPad storyboard.  Find the label and drag out two labels. You’ll immediately notice a difference in the storyboard. The labels stack.

Screenshot 2015-04-12 13.26.36

WatchKit uses a different layout system than iOS  storyboards. It will stack items below each other unless you give them one of nine positions. If two labels are in the same position, they stack. Select the top label. In the attributes inspector find Position

Screenshot 2015-04-12 13.30.27

Currently it is the left top position.  click the Horizontal  and you will see it has choices for Left, Center and Right. Select Center. Click Vertical and you will see Top, Center and Bottom. Leave it Top.  Now select the other label, and Center Horizontally.  Double click the top label and change the label to read Hello Pizza!! You should now have this:

Screenshot 2015-04-12 13.37.03

Using Emoji in an App

A simple way to add graphics to the watch is to use Emoji.  Double click in the second label. Press Control-Command-Spacebar together.  You will get the special character window:

Screenshot 2015-04-12 13.41.12

On the right of the window select  Emoji and then find Food and Drink.

Screenshot 2015-04-12 13.41.19

Select the Slice of Pizza and then double click the selection. The pizza appears in the label.

Screenshot 2015-04-12 13.45.06

Close the special character window for now. Select the pizza on the storyboard and in the attribute inspector change the Font to System and make the Size 30 points to make the pizza bigger.

Screenshot 2015-04-12 13.51.42

Adding a Date

We’ll use another object to the watch. We already have a time, let’s add a date.  Given this is a watch, one of the new objects we can drag onto the storyboard is a date. In the Object library find the date:

Screenshot 2015-04-12 13.55.34

Now drag it onto the story board.

Screenshot 2015-04-12 13.56.47

Position the date Vertical: Bottom and Horizontal: Center. You’ll notice it is giving both the date and time. We can control what it shows and how it shows it in the attributes inspector.  For those who have used NSDateFormatter before, theses choices might look a bit familiar – because the date is a label that uses NSDateFormatter. We have four choices for the date and time: None, Short, Medium, and Long. We already have a time in the status bar,  so let’s put only the date.  Change the date  to Date: Medium, Time: None .

Screenshot 2015-04-12 14.03.38

You should now have this,

Screenshot 2015-04-12 14.18.50

We are about ready to build and run the app. There are two more steps to do though. First is make sure you are running the correct scheme. At the top left of Xcode see if the device looks like this:

Screenshot 2015-04-12 16.42.02

This means we are running the watch app, not the iPhone app.

Secondly, we need to turn on the watch simulator.  You will need to have the simulator running to do this. I keep the simulator in my dock. If you do not,  you can either search for it in finder and run it (it’s hidden inside an Xcode folder)  or run the project and stop the project when the simulator appears.

Once you have the simulator running,  go to Hardware>External Displays. Then select the Apple watch you want to test with. Here I’ll test with the 38mm watch.

Screenshot 2015-04-12 16.44.21

 Once you have set up the simulator you won’t have to do this step again.  Build and run, and then click on the simulator.  Unlike a iPhone simulator, the simulator does not automatically make itself the top window – you will have to do this manually.

You should get this:

Screenshot 2015-04-12 17.06.40

You have run your first watch app.

Some of you might be seeing a different date format. This uses NSDateFormatter internally, which means it is subject to localization. If you are seeing 12 Apr 2015, that’s why.

Adding a Second Controller

To finish this up, let’s add a second controller to the storyboard. Stop the simulator, and go back to the storyboard.  You’ll find at the top of the object library an Interface Controller.

Screenshot 2015-04-12 17.11.24

Drag  an Interface Controller out, then drag two labels out onto the new controller.  There is also a timer in the object library:

Screenshot 2015-04-12 17.13.41

Drag out one of these onto the new interface controller. Your interface controller should look like this:

Screenshot 2015-04-12 17.17.06

Click on the background of the label and change the background to  green (#00ff00)

On the top label, double click on the label and type Go Run!! In the attributes inspector, Set its position Center and Top. to make the text readable change the text color to black.

On the second label, set its position bottom and center. For the content press Control-Command-Spacebar and in the  Emoji Activities and double click the Runner

. Screenshot 2015-04-12 17.27.35

Close the character window, and go back to the storyboard. Change the font to System 80 point.

Screenshot 2015-04-12 17.30.49

Select the timer. Set its position to Center,Center. Change the text color to be visible on the background. I chose a dark green(#003300). Under Timer, check the enabled on. This will start the timer as soon as the view is visible.

Your finished layout should look like this:

Screenshot 2015-04-12 17.35.46

We need to connect this by a segue. Drag a button onto our pizza scene. Set it to be Center, Center make the button back ground light gray and the text dark text. Set the title to Work Out.

Control-drag from the button to the go run scene to create a segue. You will get a menu of possible segues:

Screenshot 2015-04-12 17.41.32

Select push, which is the watch equivalent of a navigation controller.  Your storyboard should look like this:

Screenshot 2015-04-12 17.43.45

Build and Run:

pizzaRunDemo

We have working navigation and a working timer.  There’s no code in this lesson, but in our next lesson we’ll work with the buttons and other controls and start to code a more interactive watch app.