watchOS 4 教程(2):表格

原文:watchOS 4 Tutorial Part 2: Tables
作者:Audrey Tam
译者:kmyhy

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

在(1)中,你通过你的第一个 interface controller 学习了基本的 watchOS 4 开发。

在(2)中,你将添加一个表格,用于显示航班列表。

在此过程中,你将学习:

  • 如何添加新的 interface controller,添加表格,以及创建 prototype cell。
  • 如何创建一个 WKInterfaceController 子类,用于渲染表格,设置 cell 以及处理点击。
  • 如何以模态方式呈现 interface controller,并在呈现时传递数据。

好了,让我们开始吧!

注意:本教程将继续从(1)的工作上开始。你可以从上一个项目开始,或者你想从新开始的话,从这里下载项目。

开始

打开 Watch\Interface.storyboard,重新拖一个 interface controller 到画布中,位于 Flight controller 的左边。

选中新的控制器,打开属性面板,设置下列属性:

  • 设置 Identifier 为 Schedule。
  • 设置 Title 为 Air Aber。
  • 勾选 Is Initial Controller。
  • 勾选 Activity Indicator On Load。

和 Flight 控制器一样,我们也设置了这个控制器的 identifier 以便在代码中引用它。这是这个 Watch app 的第一个控制器,因此设置它的 title 并勾选 Is Initial Controller。这个控制器需要从数据源加载表格,因此需要显示 activity indicator。

接下来是 UI 部分:拖一个 table 到新的控制器上。从 document outline 中选中这个 Table Row Controller:

在属性面板中,设置它的 Identifier 为 FlightRow。当你告诉表格应该初始化哪一行时,identifier 就是那一行的类型的简单重复,因此设置 identifier 是至关重要的。

构建行的 UI

表格行实际上是一个 group,因此可以根据需要设计的很复杂。

首先要修改默认布局中的两个地方。在 document outline 中,选中表格行中的的 group,打开属性面板,将 Spacing 设置为 6,Height 设置为 Size To Fit Content。

默认表格行有一个标准的固定高度。但是,大部分时候你想在行中显示所有添加进去的 UI 元素,因此通常可以将 Height 属性修改成这样。

然后,从 Object Library 中拖一个 separator 到表格行的 group 中。实际上它不是用来分隔的,而是在表格行上添加一个可见的细线。选中这个 separator,在属性面板中修改:

  • 将 Color 设为 #FA114F (最近使用的颜色 Air Aber 粉)。
  • 将 Vertical Alignment 设置为 Center。
  • 将 Height 设为 Relative to Container。
  • 将 Adjustment 设为 –4。

属性面板现在是这样:

表格行突然会占满整个屏幕!当你布局好它后,会解决这个问题。

从 Objecdt Library 中拖一个 group 到表格行中,拖到 separator 的右边。在选中 group 的情况下,在属性面板中修改:

  • 将 Layout 设置为 Vertical。
  • 将 Spacing 设为 0。
  • 将 Width 设为 Size To Fit Content。

你可能发现了,你往往需要修改 Spacing 属性,这能将 group 中的元素之间的间距收紧,这使得小屏上的东西看起来更清晰。

拖另一个 group 到你刚刚添加的 group 中,进行下列修改:

  • 将 Spacing 设为 4。
  • 将 Height 设为 Fixed,值设为 32。

现在表格行终于变回了一个合理的高度!

然后,加一个 label 和一个 image 到刚添加的 group 中。你可以配置这个 label,然后对它进行拷贝、修改,用于显示每个航班的起点和终点。

接下来要在图片中放一些东西。下载这张图片,将它放到 Watch\Assets.xcassets 中。这会创建一个新的 image set 名为 Plane,并填充其中的 2x 空格:

你需要将这张图片变成 Air Aber 粉色,首先选中这张图片,在属性面板中,将 Render As 设为 Template Image。

回到 Watch\Interface.storyboard,在 document outline 中选中 image。在属性面板中,修改:

  • 将 Image 设为 Plane。
  • 将 Tint 设为 #FA114F。
  • 将水平对齐和垂直对齐设为 Center。
  • 将 Width 设为 Fixed,值设置为 24。
  • 将 Height 设为 Fixed,值设置为 20。

选中 label,将 Text 设置为 MEL。然后将字体修改为 System,字体风格选择 Semibold ,字号为 20。最后将垂直对齐设置为 Center。

复制粘贴 label,将它放到图片的右边。将 Text 修改为 SFO,水平对齐设置为 Right。表格行看起来是这个样子:

注意:当你复制粘贴 label 时,它会顽固地粘在 image 的左边,不管你如何在 document outline 中放置它。只要将它的水平对齐设置为 right 就可以将它移动到位。

现在的界面元素层次结构应当是这个样子:

表格行的界面基本完成,只剩下添加航班号和状态了。

从 Object Library 中再拖一个 group 到表格行中,确认它和包含起点终点 label 的 group 处于同一级。

当你后边继续构建界面时,你会发现如何用嵌套 group 加上混合布局的方式创建复杂布局。谁还用自动布局?!;]

拖两个 label 到新的水平 group 中。在属性面板中,修改左边的 label :

  • 将 Text 设置为 AA123。
  • 将 Text Color 设置为 Light Gray Color。
  • 将 Font 设置为 Caption 2。
  • 将 Vertical Alignment 设置为 Bottom。

然后,修改右边的 label:

  • 将 Text 设置为 On time。
  • 将 Text Color 设置为 #04DE71。
  • 将 Font 设置为 Caption 2。
  • 将 Horizontal Alignment 设置为 Right。
  • 将 Vertical Alignment 设置为 Bottom。

改完这些之后,表格行变成这个样子:

现在表格用 IB 创建的部分完成,接下来要渲染数据了。

渲染表格

首先需要创建一个 WKInterfaceController 来控制这个表格。

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

点击 Next、Create。

当新文件打开,删除 3 个空方法,只留下 import 语句和类定义。

回到 Watch\Interface.storyboard,选中新的 interface controller。在 Identity 面板中,修改 Custom Class\Class 为 ScheduleInterfaceController。

选中这个 interface controller,打开助手编辑器,确保代码窗口中是 ScheduleInterfaceController.swift。然后,右键从 document outline 的 Table 托一条线到 ScheduleInterfaceController 类定义中创建一个 outlet:

将 outlet 命名为 flightsTable,type 设置为 WKInterfaceTable, 然后点 Connect。

现在,你已经设置了表格的 custom class,并创建了它的 outlet,终于可以进行数据的渲染了!

关闭助手编辑器,打开 ScheduleInterfaceController.swift 在 outlet 下面添加:

var flights = Flight.allFlights()

这里,用一个属性,以 Flight 对象数组的形式保存所有航班信息。

然后,实现 awake(withContext:) 方法:

override func awake(withContext context: Any?) {
  super.awake(withContext: context)
  flightsTable.setNumberOfRows(flights.count, withRowType: "FlightRow")
}

这里,告诉表格为每一个 flights 数组中的航班对象创建一个行的实例,这个行就是你刚刚才在 IB 中构建的。行数等于数组的大小,行的类型就是你在故事板中设置的 identifier。

Build & run。你会看到表格渲染出几行数据,每一行都带有我们的 Air Aber 粉飞机:

且慢!标题是暗灰色,和 Air Aber 粉不匹配。来解决它。

打开 Watch\Interface.storyboard,选择 Air Aber interface controller。在文件面板中,将 Global Tint 设置为 #FA114F。

Build & run。效果看起来更好了!

但是,现在表格行中显示的仍然是你在 IB 中设置的占位文本。接下来你要解决这个问题,通过一个 row controller 来设置每一行中的 label。

添加 Row Controller

WatchKit 表格远比 iOS 表格简单:它没有数据源或委托!你只需要创建一个 row controller 类给它就行了,甭管它的名字,它只是一个 NSObject 子类。

在项目导航器中,右键点击 Watch Extension 文件夹,选择 New File…。在对话框中,选择 watchOS\Source\WatchKit Class 然后点击 Next。文件命名为 FlightRowController。确保他继承自 NSObject — 而不是 WKInterfaceController! — 语言选择 Swift。

点击 Next、Create。

当新文件打开,在类头部添加:

@IBOutlet var separator: WKInterfaceSeparator!
@IBOutlet var originLabel: WKInterfaceLabel!
@IBOutlet var destinationLabel: WKInterfaceLabel!
@IBOutlet var flightNumberLabel: WKInterfaceLabel!
@IBOutlet var statusLabel: WKInterfaceLabel!
@IBOutlet var planeImage: WKInterfaceImage!

这里,简单地为表格行中每个 label 添加一个 outlet。稍后你会连接它们。

然后,添加下列属性和属性观察器:

// 1
var flight: Flight? {
  // 2
  didSet {
    // 3
    guard let flight = flight else { return }
    // 4
    originLabel.setText(flight.origin)
    destinationLabel.setText(flight.destination)
    flightNumberLabel.setText(flight.number)
    // 5
    if flight.onSchedule {
      statusLabel.setText("On Time")
    } else {
      statusLabel.setText("Delayed")
      statusLabel.setTextColor(.red)
    }
  }
}

以上代码分为几个步骤:

  • 声明一个 Flight 类型的可空属性。注意,Flight.swift 是你在教程(1)中添加到 Watch Extension 中的共享代码中的一部分,
  • 一个属性观察期,用于 set 的时候被触发。
  • 如果 flight 为 nil 退出后面代码:它是一个可空,只有在明确知道 Flight 对象是有效的情况下才能继续设置 label。
  • 用 flight 的属性配置 label。
  • 如果航班延误,将文本颜色修改为红色并适当地修改文字内容。

创建了 row controller 之后,还需要修改表格行,以便用上它。

打开 Watch\Interface.storyboard,在 document outline 中选择 FlightRow。在 Identity 面板中,将 Custom Class\Class 设置为 FlightRowController。

在 document outline 中,展开 FlightRow,右键点击 FlightRow 调出 outlets 和 actions 菜单:

你可以将这个菜单往右拖,以便看见 FlightRow 上所有对象。

首先将 planeImage 属性和表格行的 image 控件连接,将 separator 和 separator 连接。然后,按照下表连接其余属性:

  • destinationLabel: SFO
  • flightNumberLabel: AA123
  • originLabel: MEL
  • statusLabel: On time

左后一步是修改 ScheduleInterfaceController,让它为表格的每一行传递一个 Flight 对象。

打开 ScheduleInterfaceController.swift,在 awakeWithContext(_:) 底部添加:

for index in 0..<flightsTable.numberOfRows {
  guard let controller = flightsTable.rowController(at: index) as? FlightRowController else { continue }

  controller.flight = flights[index]
}

这里,用一个 for 循环对表格中每一行进行遍历,并根据指定 index 获取 row controller。如果能够转换成指定的 row controller,则将之赋给一个 FlightRowController 实例。然后将 flights 数组中对应 index 的 flight 对象赋给 controller.flight。这会触发 FlightRowController 中的 didSet 观察器,从而设置表格行中的所有 label。

终于可以看看你的劳动成果了:Build & run。你会看见现在表格行中显示了每个航班的相关信息:

然后就该是本教程的最后一个步骤了:当用户点击表格行,ScheduleInterfaceController 应该将对应的航班传递给航班详情界面并彰显它,而这个界面在上一个教程中已经创建好了。

响应行点击事件

首先需要覆盖 WKInterfaceTable 方法中的一个专门负责处理行点击事件的方法。

在 ScheduleInterfaceController 添加方法:

override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
  let flight = flights[rowIndex]
  presentController(withName: "Flight", context: flight)
}

这里,先从 flights 数组中根据方法的 index 参数找出对应的航班对象。然后呈现航班详情页面,将 flight 对象作为上下文传递给它。注意,presentController(withName:context:) 方法中的 name 是你在上一部教程中在故事板中设置的 identifier。

然后,和教程(1)一样,需要修改 FlightInterfaceController,用这个上下文设置界面。

打开 FlightInterfaceController.swift,找到 awake(withContext:)的这一句:

flight = Flight.allFlights().first

将它替换为:

if let flight = context as? Flight {
  self.flight = flight
}

这里,尝试将 context 转换成 Flight 实例。如果转换成功,将它赋给 self.flight,这将触发该属性的观察器,并配置界面。

本教程的最后一步是 build & run。点击其中的表格行,你会看到航班详情页面显示了所选航班的信息:

恭喜你!你已经实现了你的第一个表格,并用真实数据来渲染了它。值得鼓励!

接下来做什么?

这是到目前为止已经完成的项目

在这部教程中,你学习了如何在 interface controller 中添加表格,构造表格行的界面,创建 row controller,处理行点击事件,显示新的 interface controller,以及通过上下文传递参数。哎呀,在 20 分钟的时间内需要记住这么多东西。

接下来呢?肯定就是本教程的(3)了。在这里,你将学习的课程全都和 watchOS 动画有关。

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

猜你喜欢

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