watchOS 2 教程(一):开始吧

原文:watchOS 2 Tutorial Part 1: Getting Started

这一年的 WWDC 大会上,苹果公司推出了 watchOS 2,这标志着 Apple Watch 的开发产生了巨大的变化。现在,你可以开发能运行在你手表上原生的 app 了。

在这篇 watchOS 2 教程中,你会开发一个简单但是功能齐全的 watchOS 2 的 app。

在这个过程中,你会学到:

  • 如何为 iOS app 添加 watchOS 2 的 target
  • 如何在两个 target 之间共享数据
  • 如何添加一个 watchOS 2 界面控制器到 storyboard,并放置界面对象
  • 如何创建 WKInterfaceController 的子类并连线
正式开始吧

首先下载教程的起始项目吧。

在Xcode中打开它然后编译运行。你应该会看到一个空白界面:

这个项目没有太多的文件,只包含一些你需要的最基本的文件。

添加 WatchKit App

选择 File\New\Target… ,在出现的对话框中选择 watchOS\Application\WatchKit App 然后点击 Next:

在接下来的界面中,设置项目名字为 Watch,确保语言设置为 Swift,然后取消选中任何复选框。点击 Finish:

之后会询问你是否想要激活 watch scheme,你需要这么做,所以确保选择了激活:

祝贺,你刚刚创建了你的第一个手表 app!这真的很容易。

你会注意到,这个操作实际上创建了两个 target,而不是一个,在项目导航中看到两个对应的组。这是因为手表 app的代码实际是作为一个扩展形式存在的,类似 iOS 上的 Today extensions。

当你在项目导航中点开 Watch 和 Watch Extensions 组的时候,你会看到所有 storyboard 放在 Watch 组,当前 target 创建的所有的类文件放在 Watch Extensions 组中:

你需要遵循如下的原则:任何你添加的代码必须放在 Watch Extension 组中然后添加到 Watch Extension target,而所有的 assets 或者 storyboards 需要放在 Watch 组里。

小家务

在继续前,你需要删掉一些 target 模板添加的你不需要的文件。

在项目导航中右键点击 InterfaceController.swift 然后选择删除。
当弹出提示,选择 Move to Trash 来确保文件确实从项目中删掉了:

下一步,打开 Interface.storyboard,选择其中仅有的界面控制器,按下 backspace 键来删除它。现在就剩下一个空 storyboard,或者是我认为的,一个空白画布。

共享数据和代码

起始项目包含一个记录所有 Aber 航空公司航班信息的 JSON 文件,一个模型类表示飞行数据。这正是应该共享的数据,因为 iOS app 和手表 app 使用相同的模型类和数据-你记得 DRY (不要写重复的代码)原则吗?

在项目导航中点开 Shared 组然后选择 Flights.json。之后,在 File Inspector 中找到 Target Membership 区域,选中 Watch Extension:

文件现在应该被 AirAber 和 Watch Extensions 这两个 target 所包含。
为其他 Shared 组的文件重复这个步骤,比如说 Flight.swift。
这些都做完后,你可以开始开发航班详情界面了!

构造界面

打开 Watch\Interface.storyboard,从对象库拖一个界面控制器到 storyboard 里面.选中这个界面控制器,打开属性检查器设置它的 Identificer 为 Flight,然后勾选 Is Initial Controller:

你设置的这个 Identifier 让你可以在代码中引用这个界面控制器。选中 Is Initial Controller 简单告诉 WatchKit 你希望当应用程序启动的时候首先显示这个界面。

下一步,从对象库中拖动一个组到界面控制器:

之后这个组会包含 Aber 公司的 logo,航班号和路线。

选中这个组,在属性检查器的顶部改变它的 Insets 为 Custom。这会显示四个额外的文本框让你可以手动的设置组的上下左右。设置 Top 为6:

这仅仅让你的组到顶部有个额外的空隙。

下一步,拖动 Image 到组中。组会相应的收缩来改变 Top inset (感谢 Xcode!),之后在文档大纲中检查来确保 Image 是组的子节点,而不是同级:

现在需要显示一张图片,下载 logo 图片 然后把它拖动到 Watch\Assets.xcassets 中。这会创建一个新的 logo 图片,存放在2x的部分。

为了给图片染色,选中这张图片,在属性检查器中修改 Render As 为 Template Image。

重新打开 Watch\Interface.storyboard 选中之前的 image.使用属性检查器,做如下的改变:

  • 设置图片为 Logo - 当下拉列表没有出现,你可以自己输入;
  • 设置 Tint 为 #FA114F(也可以在颜色面板中输入值);
  • 设置 Width 为 Fixed,值为40;
  • 设置 Height 为 Fixed,值为40。

属性检查器现在应该像下面这样:

不要担心看不到 logo,因为 Xcode 设计时无法给模板图片染色!

下一步,往已经存在的组中拖动另外一个组,确保它出现在 image 的右侧,使用属性检查器设置 Layout 属性为 Vertical.同样修改 Spacing 为0、Width 为 Size to Fit Content。然后拖动两个 label 到新的组中,放置一个到另一个的下面。

选择上面的 label,使用属性检查器,设置文本为 Flight 123,文字颜色为 #FA114F。

选择下面的 label,设置文本为 MAM to SFO。界面控制器最后看起来像下面这样:

这些文本仅仅充当占位符,之后会被控制器中设置的文本取代。

下一步,拖动另一个组到界面控制器中,但是这次确保与第一个组同级。当不能设置组级别关系请使用文档大纲(Document Outline)。

选中新的组,设置它的 Layout为 Vertical、Spacing 为0。

现在,拖动三个 label 到新的组中:

确保 label 都在 group 中,而不是与 group 同级!

选择顶部的 label 使用属性检查器修改它的文本为 AA123 Boards。

选中中间的 label,修改文本颜色为 #FA114F,字体选择 System,Regulaer 样式和54.0的 size.最后,修改 Height 为 Fixed,值是44。

选中底部的 label 修改文本为 On time,文本颜色为 #04DE71。

你的界面控制器应该现在像下面这样:

从对象库中拖动一个新的组到下面的组,这次确保它是在子节点而不是在同级,之后向其中添加两个 label,你完全的界面对象关系应该像这样:

使用属性检查器,设置左边的 label 文本为 Gate 1A。右边的 label 设置为 Seat 64A,之后设置它的 Horizontal alignment 为 Right

完全的界面应该像如下这样:

恭喜,你已经完成你的第一个 watch app 界面的布局了,现在是时候给它填充一些真实的数据然后在模拟器上运行。

创建控制器

在项目导航中右击 Watch Extensions 组,选择 New File,在出现的对话框中选择 watchOS\Source\WatchKit Class 然后点击 Next。命名新的类为 FlightInterfaceController ,确保它为 WKInterfaceController 的子类,语言设置为 Swift:

点击 Next,之后是 Create

可以看到新的文件在代码编辑器中打开了,删除其中的三个空方法,只剩下 import 语句和类定义。

添加这些 outlets 到 FlightInterfaceController 的顶部:

@IBOutlet var flightLabel: WKInterfaceLabel!
@IBOutlet var routeLabel: WKInterfaceLabel!
@IBOutlet var boardingLabel: WKInterfaceLabel!
@IBOutlet var boardTimeLabel: WKInterfaceLabel!
@IBOutlet var statusLabel: WKInterfaceLabel!
@IBOutlet var gateLabel: WKInterfaceLabel!
@IBOutlet var seatLabel: WKInterfaceLabel!

这里仅仅为之前的每个 label 添加一个 Outlet。稍后会把他们连接起来。

下一步,在 outlets 下面添加 flight 属性和对应的属性观察器:

// 1
var flight: Flight? {
  // 2
  didSet {
    // 3
    if let flight = flight {
      // 4
      flightLabel.setText("Flight \(flight.shortNumber)")
      routeLabel.setText(flight.route)
      boardingLabel.setText("\(flight.number) Boards")
      boardTimeLabel.setText(flight.boardsAt)
      // 5
      if flight.onSchedule {
        statusLabel.setText("On Time")
      } else {
        statusLabel.setText("Delayed")
        statusLabel.setTextColor(UIColor.redColor())
      }
      gateLabel.setText("Gate \(flight.gate)")
      seatLabel.setText("Seat \(flight.seat)")
    }
  }
}

一步步讲解发生的事情:

  1. 你定义了一个可选的属性类型为 Flight。这个类在 Flight.swift 中定义;
  2. 你添加了一个属性观察器,当属性设值时候会触发它;
  3. 在可选属性中确保有一个真的 flight 而不是 nil,当 flight 存在才会去设置 labels 的值;
  4. 使用 flight 的相关属性去设置 labels
  5. 如果航班被延误,那么你就将标签的文本颜色改为红色

在控制器第一次显示时候设置航班。添加以下声明:

override func awakeWithContext(context: AnyObject?) {
  super.awakeWithContext(context)
  flight = Flight.allFlights().first!
}

本后面的教程会修改为在上下文中传递值给它,但现在你只需要从共享的 JSON 文件中加载所有的航班,然后使用数组中的第一个。

在后面的教程你将学到更多关于 awakeWithContext(_:) 的知识,但是现在你仅仅需要知道它是界面控制器生命周期第一环节,一个设置 flight 值的地方。

现在仅剩最后一步你就可以编译运行了,就是去连接 outlets

连接outlets

打开 Watch\Interface.storyboard 选择界面控制器,使用 Identity Inspector,设置 Class\Custom Class 为 FlightInterfaceController

下一步,右击界面控制器顶部的黄色图片弹出窗口:

现在,按下面的列表连接 outlets:

boardingLabel: AA123 Boards
boardTimeLabel: 15:06
flightLabel: Flight 123
gateLabel: Gate 1A
routeLabel: MAN to SFO
seatLabel: Seat 64A
statusLabel: On time

在运行之前,有一件事情要做。本教程的实例 app 专为42mm 的 Apple Watch 开发的,所以你需要确保正确设置了模拟器,否则界面元素看起来会有点小。对于一个现实 app,需要确保界面能很好运行在两种大小的手表上,但这在本教程的范围之外。

在 Xcode 中,选择 Window\Devices 打开设备管理器,点击右下角的 + 图标.在弹出的对话框中,命名模拟器为 iPhone 6 - 42mm,设置设备类型为 iPhone 6,修改配对的 Apple watch 为 Apple Watch – 42mm (WatchOS 2.0) 然后点击 Create:

关闭设备管理器,选择 Watch Scheme,然后选中新的模拟器:

编译运行。一段模拟器启动完成你会看到下面界面:

注意:如果收到一条错误消息,说明安装失败,然后你可以再次尝试使用 Xcode,或者在手表模拟器上手动安装 app。为此,打开 iOS 模拟器中的手表 app,点击 AirAber ,在 Apple Watch 弹出我们的 app。一旦这么做了,返回手表模拟器,按 Shift + Ctrl + H 导航到主界面, 然后点击 AirAber 图片来启动手表 app。

恭喜!你已经完成 WatchKit 初始界面,并使用真实的数据使它很好运行在手表模拟器上。

接下来做什么?

下面是这个系列教程完整示例项目

在这个练习中你已经学会了如何往现有的 iOS app 中添加手表 app,如何创建一个界面控制器和使用嵌套组构造一个非常复杂的界面,以及使用 WKInterfaceController 类来配合这项工作。那么,接下来呢?

本教程系列的第二部分,你将学习所有关于表和导航 WatchKit 的使用。

如果您对本教程有任何疑问或意见,请参加下面的论坛进行讨论!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,719评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,337评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,887评论 0 324
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,488评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,313评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,284评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,672评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,346评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,644评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,700评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,457评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,316评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,706评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,261评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,648评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,859评论 2 335

推荐阅读更多精彩内容