watchOS 4 教程(1):开始

原文:watchOS 4 Tutorial Part 1: Getting Started
作者:Audrey Tam
译者:kmyhy

更新说明: 本教程由 Audrey Tam 更新至 Swift 4/watchOS 4。原文作者是 Mic Pringle。

在这篇教程中,你将编写一个简单但功能齐备的 watchOS app。尤其是,你将为一个虚拟的航空公司 Aire Aber 编写一个 watch app。

在这个过程中,你将学习:

  • 如何在一个 iOS app 中添加一个 watchOS 4 target。
  • 如何在两个 target 将共享数据。
  • 如何在故事板中添加一个 watchOS 4 interface controller,并布局 UI 对象。
  • 如何创建 WKInterfaceController 子类,并将所有东西绑定在一起。

让我们开始吧!

开始

首先下载本教程的开始项目

用 Xcode 打开项目,Build & run。你会看到一个空白屏幕:

这个项目没有多少东西:除了几个助手类文件就没有什么了。我们将从这里开始!

添加 WatchKit App

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

在后面的窗口中,设置 Product Name 为 Watch,Language 为 Swift, 然后清除所有选项框,点击 Finish:

然后问你是否想激活 watch scheme,这时请点击 Activate:

恭喜你,你已经创建了你的第一个 Watch app!其实非常简单。

你会看到,这其实会在项目导航器中创建两个而不是一个 target,以及两个对应的 group。这是因为 watch app 中的代码实际上是以一种扩展包的方式运行的,和 iOS 中的今日扩展是一样的。

展开项目导航器中的 Watch 和 Watch Extension 文件夹。你会看到 watch group 中有一个故事版,而类被创建在了 Watch Extension group 中。

这就是你今后需要遵循的规矩:所有代码必须放在 Watch Extension 文件夹,并添加到 Watch Extension target,而任何 assets 或者故事板必须放在 Watch 文件夹。

作业

在继续之前,你必须将 target 模板中添加的一些东西移除,这些东西你将用自己的替换。

在项目导航器中,右击 InterfaceController.swift,选择 Delete。当提示你的时候,选择 Move to Trash,确保这些文件真的从项目中删除:

接着,打开 Interface.storyboard,选中其中唯一的一个 interface controller,按下 delete 键。只留下一个空的故事板,或者我会称之为空白的画布。

共享数据和代码

开始项目包含了一个包含了所有 Air Aber 航班信息 JSON 文件,以及用于表示这些数据的模型类。这可能是你会在多个 target 中共享的东西,因为很可能 iOS app 和 watch app 都要用到同一个模型类和数据——还记得 DRY(不要重复造轮子)原则吗?

在项目导航器展开 Shared 文件夹,选择 Flights.json。然后,找到 File 下面的 Target Membership,勾上 Watch Extension:

这个文件现在同时包含到了 AireAber 和 Watch Extension target 中。

在 Shared group 的另一个文件 Flight.swift 上重复同样动作。

然后,就可以构建航班详情页面了!

构建 UI

打开 Watch\Interface.storyboard,从 Object Library 中拖一个 interface controller 到画布中。选中这个 interface controller,打开属性面板,将 Identifier 设置为 Flight,然后勾上 Is Initial Controller。反选 Activity Indicator On Load:

我们在这里指定了 identifier 以便在代码中引用这个 interface controller。勾选 Is Initial Controller 是告诉 WatchKit 这是 app 一开始启动时显示的第一个 controller。这个界面不会下载任何数据,因此不需要显示 activity indicator。

为了简单起见,你的布局只需要针对 42mm 表盘。如果是你自己的 app,你可能需要需改这个以便适应所有的手表尺寸。在故事板的左下角,确保显示的是 View as: Apple Watch 42mm。

Watch app 的布局和 iOS 的布局完全不同。首先:你不能通过拖放的方式移动或修改 UI 对象的大小。当你拖某个对象到 controller 上时,它会紧跟在上一个对象之后,屏幕会快速占满。为了对齐它们,你需要用 group,类似于 iOS 和 macOS 中的 stack view。

因此,从 Object Library 中拖一个 group 到 interface controller 中:

尽管它现在还看不出什么门道,但它将用于容纳 Air Aber 的 logo、航班次数以及航线。

选中这个 group,打开属性面板,将 Insets 修改为 Custom。这会显示 4 个text field,你可以用它们手动设置 上、下、左、右的留白。

将 Top 修改为 6:

这将在布局 group 的上端保留一定的留空。

然后拖一张 image 到 group。如果你的 group 因为 top inset 修改之后被压扁了(感谢 Xcode),那么可以将它拖到 document outline 窗口中,并确保它是一个 group 中的子对象,而不是平级对象:

现在需要显示一张图片。从这里下载 logo 图片,将它拖进你的 Watch\Assets.xcassets 中。这会创建一个 image set 叫做 logo,在它的 2x 图上会显示一张图片:

你还想将图片上色,将它的颜色变成 Air Aber 公司的主题色,因此可以选中这张图片,在属性面板中,设置 Render As 为 Template Image。

重新打开 Watch\Interface.storyboard,然后选择 image 对象,在属性面板中,修改:

  • 将 Image 设置为 Logo——如果在下拉框中没有 Logo,直接输入 Logo。
  • 将 Tint 设置为 #FA114F(在 Color RGB 滑动面板中输入)。
  • 将 Width 设置为 Fixed,值输入 40。
  • 将 Height 设置为 Fixed,值输入 40。

现在属性面板应当是这个样子:

如果你看不见 logo 也不用担心:在设计时 Xcode 还不会对 template image 进行着色!但相信我,当你 build & run 之后,它会显示成鲜艳的品红色。

然后,拖一个 group 到现有的这个 group 中,确保它显示在 image 的右边,然后用属性面板将它的 Layout 修改为 Vertical。同时,改变 Spacing 为 Custom\0 然后 Width 设置为 Size to Fit Content。

然后,拖两个 label 进新的 group 中。因为你将布局设置为垂直,它们会成剩下排列:

选中上面的 label,在属性面板中,设置它的 Text 为 Flight 123,Text Color 为 #FA11AF(你也可以不输入这个值,而是从 Color 菜单的最近使用过的颜色中选择)。

然后,选中第二个 label,将 Text 设置为 MEL to SFO。你的 interface controller 现在应该是这个样子:

它们上面的文字只是一个占位字符,当我们将 UI 和 controller 类进行关联之后,这些文字将被替换。

继续,拖另外一个 group 到 interface controller 中,这次将它作为第一个 group 的同级。如果你无法将它拖到正确的地方,请在 document outline 中进行操作。

选中新的 group,设置它的 Layout 为 Vertical,Spacing 设置为 Cutom\0。

然后,拖 3 个 label 到这个 group:

用 document outline 去检查这 3 个 label 位于这个 group 的下面,而不是和这个 group 平级。

选择最上面的那个 label,在属性面板中修改它的 Text 为 AA123 Boards。

然后,选中中间的那个 label,将 Text 修改为 15:06。将 Text Color 修改为 #FA114F,字体为 Sytem,字体风格为 Regular,字号为 54。最后,修改高度为 Fixed,值为 44。

选中最底下的那个 label,修改它的 Text 为 On Time,文字颜色为 #04DE71。

你的 interface controller 应当是这个样子:

在开始创建 outlet 并让界面显示真实数据之前,还需要再添加一个 group。

从 Object Library 中拖一个新的 group 到下面的这个 group 中,这次它应当位于下级而不是平级,然后它的位置将位于父 group 的最下面。然后,在里面添加两个 label。最终 interface 对象树变成这个样子:

将左边 label 的 Text 设置为 Gate 1A。右边 label 的 Text 设置为 64A,并将 Horizontal Alignment 设置为 Right。

这时的界面变成这个样子:

恭喜,你已经完成了你第一个 Watch app 的布局!接下来让它显示真实数据,让它在模拟器中跑起来。

创建控制器

在项目导航器中,右键点击 Watch Extension 文件夹,选择 New File…。在对话框中,选择 watchOS\Source\WatchKit Class 然后点击 Next。命名这个类为 FlightInterfaceController,继承自 WKInterfaceController,语言设置为 Swift:

点击 Next,然后 Create。

当新建文件的编辑窗口打开,删除其中的 3 个空方法,只留下 import 语句和类定义。

添加以下 outlet:

@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。等会你会连接它们。

然后,添加这个属性和属性观察器:

// 1
var flight: Flight? {
  // 2
  didSet {
    // 3
    guard let flight = flight else { return }
    // 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(.red)
    }
    gateLabel.setText("Gate \(flight.gate)")
    seatLabel.setText("Seat \(flight.seat)")
  }
}

这里一步一步解释如下:

  1. 声明一个可空的 Flight 类型属性。这个类在 Flight.swift 中定义,它是我们前面添加到 Watch Extension target 中的共享代码之一。
  2. 声明一个属性观察器,当这个属性被赋值时会触发。
  3. 这句确保 flight 不为 nil。只有当明确有一个 Flight 对象时我们才需要配置这些 label.
  4. 用 flight 的对应属性配置 label。
  5. 如果航班延期,你将 label 的显色修改为红色。

然后你要在控制器第一次显示时对 flight 进行赋值。在 flight 属性之后继续添加:

override func awake(withContext context: Any?) {
  super.awake(withContext: context)

  flight = Flight.allFlights().first
}

在本系列的(2) 中,你将修改这个实现以便使用参数中的 context,但目前,我们只需要从共享的 JSON 文件中加载所有的航班,并从数组中取出第一个对象。

注意:awake(withContext:) 方法在控制器从故事板中加载并创建好所有 Outlet 之后调用,因此这是一个设置 flight 的好地方。

现在,在 build & run 之前还有一件事情要做,就是连接这些 outlet。

连接 outlet

打开 Watch\Interface.storyboard 选中 interface controller。在 Identity 面板中,
将 Custom Class\Class 设为 FlightInterfaceController。

然后,用你自己的方式连接下面的 outlet:

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

在点击 Run 之前,还有一件事情。示例 app 是为 42mm 手表设计的,因此需要配置成正确的手表模拟器,否则有的东西看起来会不正常。在真实的 app 中,你可能想让界面在两种手表尺寸上都能工作,但那不是本文讨论的内容。

打开 Watch scheme 菜单,选择 42mm 模拟器。

Build & run。当模拟器启动后,你会看到你的精心布局,显示了应 Air Aber 品红着色的 logo。Flight 对象的起飞时间和座位是随机生成的,因此你看到的值可能是不同的:

注意:如果你看到错误信息,显示安装失败,你可以重试一次,或者手动在 watch 模拟器上安装。要手动安装,在 iOS 模拟器上打开这个 Watch app,按住 AirAber,然后将 Show App on Apple Watch 设置为 On。然后回到 watch 模拟器,点击 Digital Crown 回到 Home 界面,然后点击 AirAber 图标以打开 app。

恭喜,你已经完成了你的第一个 WatchKit 界面,并在 watch 模拟器上以真实数据运行了 app——干得不错:]

接下来做什么?

这里下载到现在为止已完成的示例项目。

在这个例子中,你学习了如何添加 Watch app 到已有的 iOS app 中,如何创建一个 interface controller 以及用嵌套 group 的方式对一个相对复杂的界面进行布局,以及如何通过 WKInterfaceController 将所有东西连接在一起。那么,接下来呢?

接下来当然是本系列的(2)了!在(2)中,你将学习 table 以及如何在 WatchKit 中进行页面导航。

如果你有任何问题或建议,请在下面留言:]

猜你喜欢

转载自blog.csdn.net/kmyhy/article/details/79485911