原文:watchOS 4 Tutorial Part 3: Animation
作者:Audrey Tam
译者:kmyhy更新说明: 本教程由 Audrey Tam 更新至 Swift 4/watchOS 4。原文作者是 Mic Pringle。
欢迎回到 watchOS 4 系列教程!
在教程(1)中,你学习了基本的 watchOS 4 开发,创建了你的第一个 interface controller。
在教程(2)中,你学习了如何在 app 中添加表格。
在这部教程(3)中,你将学习如何使用 watchOS 4 动画,在 app 中添加一个新的登机界面。
在这个过程中,你将学到:
让我们立即开始吧!
注意:这部分教程基于教程(2)的工作基础之上。你可以用之前的项目开始,如果你想重新开始,也可以从这里下载代码。
打开 Watch\Interface.storyboard,从 Object Library 中拖一个 interface controller 到故事板中。选中这个 interface controller,打开属性面板,将 Identifier 设置为 CheckIn。这样才能从 ScheduleInterfaceController 中呈现这个 interface controller。
然后,拖一个 group 到新的 interface controller 上。在属性面板中,修改属性:
你的 interface controller 看起来应该像这样:
接下来,我们会构建一个 label-image-label 样的 group,和你创建表格行时差不多。
拖一个 group 到刚才的 group 中,在属性面板修改下列属性:
在新 group 中添加一个 label 和一个 image。我们会设置 label,然后复制修改它,用于显示航班起点和终点。
选中 image,无论从故事板中还剩 document outline 中都可以。在属性面板中,修改其属性:
和之前一样,这个图片不会被上色,你无法将它从 interface controller 的黑色背景中看见它。但你应该知道它是存在的。
选中 label,将 Text 设置为 MEL。将 Font 设置为 System,字体样式为 Semibold、大小为 20。最后,设置它的 Vertical alignment 为 Center,将它的宽、高都修改成 Size To Fit Content。
复制这个 label,然后将它粘贴到 image 旁边。将 text 修改为 SFO,Horizontal Alignment 设置为 Right。你的 interface controller 现在应该看起来是这个样子:
现在来添加一个大大的登机按钮。
从 Object Library 中拖一个按钮到 interface controller,将位置放到和起点、终点 label 同一个 group 中:
WatchKit 中的按钮非常灵活;你可以只使用它们的内置样式——这样你只能添加一种样式——你还可以将它们转换成布局 group,将其它界面元素添加进去以定制其外观。这就是我们接下来要做的。
选中按钮,在属性面板中修改下列属性:
现在的 interface controller 应该是这个样子:
你可能注意到了,当你修改按钮的 Content 属性时,在 document outline 中出现了一个新的 group:
你将用它作为登机按钮的背景。选中这个 group,在属性面板中修改属性:
interface controller 现在是这个样子:
登机按钮开始有点样子了。唯一缺少的仅仅是一个标签,接下来我们就添加它。
拖一个 label 到按钮下面的这个 group 中,然后选中 label。用属性面板修改其属性:
完成后的这个登机按钮长这个样子:
接下来应该专门为这个 controller 创建一个 WKInterfaceController 子类并修改 ScheduleInterfaceController 以便显示它。
在项目导航器中,右键点击 Watch Extension 文件夹,选择 New File…。在对话框中选择 watchOS\Source\WatchKit Class 然后点 Next。文件名命名为 CheckInInterfaceController,继承自 WKInterfaceController,将 Language 设置为 Swift。
点击 Next、Create。
新文件打开后,删除 3 个空方法,只留下 import 语句和类定义。
然后,在类头部添加:
@IBOutlet var backgroundGroup: WKInterfaceGroup!
@IBOutlet var originLabel: WKInterfaceLabel!
@IBOutlet var destinationLabel: WKInterfaceLabel!
这里为最外层的 group 和两个 label 都创建了 outlet。后面我们会连接它们。
然后,在 outlet 下面添加:
var flight: Flight? {
didSet {
guard let flight = flight else { return }
originLabel.setText(flight.origin)
destinationLabel.setText(flight.destination)
}
}
你应该知道了吧!这里,我们添加了一个可空的 Flight 对象,同时定义了一个属性观察器。当观察器触发时,我们队 flight 解包,如果解包成功,用这个 flight 来设置两个 label。这个套路你已经相当熟悉了,不是吗?
当 controller 被呈现时,我们必须对 flight 属性赋值。在 CheckInInterfaceController 中添加方法:
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let flight = context as? Flight {
self.flight = flight
}
}
这个你也应该很熟悉。我们首先解包 context 对象成一个 Flight 对象。如果解包成功,将它赋给 self.flight,这会触发属性观察器并更新 UI。
最后,在 awake(withContext:) 下面添加:
@IBAction func checkInButtonTapped() {
// 1
let duration = 0.35
let delay = DispatchTime.now() + (duration + 0.15)
// 2
backgroundGroup.setBackgroundImageNamed("Progress")
// 3
backgroundGroup.startAnimatingWithImages(in: NSRange(location: 0, length: 10),
duration: duration,
repeatCount: 1)
// 4
DispatchQueue.main.asyncAfter(deadline: delay) { [weak self] in
// 5
self?.flight?.checkedIn = true
self?.dismiss()
}
}
分成几个步骤:
现在需要在项目中添加用到的图片,以及连接 outlet 和一个 IBAction。
下载这个 zip 文件,解压缩,将文件夹整个拖到 Watch\Assets.xcassets。
确认你拖的是文件夹,而不是它的内容。这会在 asset catalog 中创建一个新的 group 叫做 Progress,其中包含以下几张图片:
添加完图片,就要连接 outlet 和按钮的 action 了。
打开 Watch\Interface.storyboard,选中我们的新 interface controller。在 Identity 面板中,修改 Custom Class\Class 为 CheckInInterfaceController:
在 document outline 中,右键点击 CheckIn 调出 outlets and actions 菜单。将 backgroundGroup 连接到 interface controller 最外层的 group:
在故事板中,连接 destinationLabel 到 SFO 标签,连接 originLabel 到 MEL 标签。
然后,连接 checkInButtonTapped 到圆圆的大按钮上:
在 Build & run 之前,还有最后一件事情,就是呈现这个 interface controller。
打开 ScheduleInterfaceController.swift,找到 table(_:didSelectRowAt:) 方法,将它的内容修改为:
let flight = flights[rowIndex]
let controllers = ["Flight", "CheckIn"]
presentController(withNames: controllers, contexts: [flight, flight])
这里,我们从 flights 数组检索出 rowIndex 对应的 flight 对象,创建一个包含了要呈现的 2 个 interface 控制器的 identifier 的数组,并分别向它们传入两个 flight 作为 context。
Build & run。点击一个航班,你会两个 interface controller 显示出来。向左扫显示登机 controller,点击按钮播放动画并登机:
这看起来很好,但如果在 schedule interface controller 中能对已登机的航班加亮显示就更好了。我们将在下一节也就是本文的最后一节解决这个问题。
打开 FlightRowController.swift 添加下列方法:
func updateForCheckIn() {
let color = UIColor(red: 90/255, green: 200/255, blue: 250/255, alpha: 1)
planeImage.setTintColor(color)
separator.setColor(color)
}
这里我们创建了一个 UIColor 实例,用它设置 planeImage 和 separator 的 tint 颜色。这个方法将在动画闭包中调用,因此颜色的改变是以动画的方式进行的。
然后,打开 ScheduleInterfaceController.swift 在 flights 属性后面添加:
var selectedIndex = 0
用这个属性记录当呈现两个 interface 控制器时选中的行。当表格行被选中时对这个属性赋值就好了。在 table(_:didSelectRowAt:) 中调用 presentController(withNames:contexts:) 的一句前添加:
selectedIndex = rowIndex
将 selectedIndex 设置为选中行。
最后,在 ScheduleInterfaceController 的 awake(withContext:) 方法后面添加:
override func didAppear() {
super.didAppear()
// 1
guard flights[selectedIndex].checkedIn,
let controller = flightsTable.rowController(at: selectedIndex) as? FlightRowController else {
return
}
// 2
animate(withDuration: 0.35) {
// 3
controller.updateForCheckIn()
}
}
代码解释如下:
Build & run。执行前面同样的步骤进行航班登机。当你返回 schedule interface controller,你会发现飞机图片和分隔线的颜色以一种渐变的方式发生了改变。
恭喜你!你已经完成了你的第一个 WatchKit 动画。
这里是本系列完成后的示例项目。
在这部分教程中,你学习了如何创建两个不同的 WatchKit 动画。第一个使用的是动画图片序列,第二个使用的是 WKInterfaceController 的 animation API。你现在已经可以为你自己的 watchOS 4 app 中添加大量的视觉效果了!
遗憾的是,本系列教程就到此为止了。
如果你有疑问或建议,请在下面的论坛中留言!