WatchOS 总结
建立 WatchOS 项目
- 创建新的 iOS App 和 WatchKit App: 选择 File\New\Project…,在出现的对话框中选择 watchOS\Application\iOS App with WatchKit App 然后点击 Next
- 为已有的 iOS App 添加 WatchKit App: 选择 File\New\Target…,在出现的对话框中选择 watchOS\Application\WatchKit App 然后点击 Next
以上的操作创建了 WatchKit App 和 WatchKit Extension 两个 Target。其中 View 在 WatchKit App 里,而 Controller 和 Model 在 WatchKit Extension 里。
用户交互方式
WKInterfaceController
生命周期
public func awakeWithContext(context: AnyObject?)
是开始初始化 Storyboard 中的 UI 时加载的方法,可以在这里加载数据或者更新在 StoryBoard 中当前 Controller 添加的 interface objects。
public func willActivate()
是显示当前加载的UI时加载的方法,可以在这里更新 interface objects 或者处理其他事件。
public func didDeactivate()
是切换页面或者停止使用时加载的方法,可以在这里清理 task 或者数据。
页面跳转
1
2
3
4
5
6public func pushControllerWithName(name: String, context: AnyObject?)
public func popController()
public func popToRootController()
public func presentControllerWithName(name: String, context: AnyObject?)
public func presentControllerWithNames(names: [String], contexts: [AnyObject]?)
public func dismissController()
Push、Pop、Present、Dismiss 的行为和 UIViewController 中类似,在 Storyboard 中为 Push、Modal 两种形式。presentControllerWithNames 我们可以 present 一组 Controller, 这一组 Controller 将以 page control 的形式展示。
public func becomeCurrentPage()
是当页面以 page control 的形式展现时,可以调用这个方法改变当前的 page 来跳转页面。在 Storyboard 中是以 next page 实现页面跳转。
1
2
3
4public func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject?
public func contextsForSegueWithIdentifier(segueIdentifier: String) -> [AnyObject]?
public func contextForSegueWithIdentifier(segueIdentifier: String, inTable table: WKInterfaceTable, rowIndex: Int) -> AnyObject?
public func contextsForSegueWithIdentifier(segueIdentifier: String, inTable table: WKInterfaceTable, rowIndex: Int) -> [AnyObject]?
如果直接在 Storyboard 中通过 Segue 实现了页面跳转,可以通过上边这些方法进行数据传递。在目的页面public func awakeWithContext(context: AnyObject?)
方法中接收传值,其中context
就是传递过去的值。
Glance 界面
将重要信息展示在一个视图里,让用户能在一瞥之间快速获取,是用户浏览 Watch app 中的重要信息的补充方式,对于 Watch app 并不是必需的。当添加 Watch app target 到你的 iOS App 中时,你能指定是否需要一个 Glance 界面。如果你在一开始忘了添加,也可以稍后添加。一个 Watch app 只允许有一个 Glance 界面控制器
由于 Glance 的界面是由自定义的 WKInterfaceController 子类驱动的,所以 Glance 界面控制器的生命周期和其他 WatchKit 界面控制器一样。但是 Glance 不支持互动操作,触摸 Glance 将启动对应的 Watch app。
如果你想直接跳转到指定的页面,需要在 Glance 界面的 willActivate 方法中调用public func updateUserActivity(type: String, userInfo: [NSObject : AnyObject]?, webpageURL: NSURL?)
方法,并通过 userInfo 参数指定传输的信息,然后在主控制器 InterfaceController 实现public func handleActionWithIdentifier(identifier: String?, forRemoteNotification remoteNotification: [NSObject : AnyObject])
或者public func handleActionWithIdentifier(identifier: String?, forLocalNotification localNotification: UILocalNotification)
方法,使用提供的 userInfo 字典定义 UI。
Notification 界面
Apple Watch 中的通知分为静态通知和动态通知。静态通知只显示应用图标和简单的文本信息。当你抬起手腕或者点击屏幕时就会进入动态通知界面,你就可以看到该通知更多详细的信息。
WKUserNotificationInterfaceController 继承于 WKInterfaceController,享有和 WKInterfaceController 同样的生命周期。也可以通过之前 Glance 界面的方法跳转指定界面。
更可以通过public func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], withCompletion completionHandler: (WKUserNotificationInterfaceType) -> Void)
或者public func didReceiveLocalNotification(localNotification: UILocalNotification, withCompletion completionHandler: (WKUserNotificationInterfaceType) -> Void)
获得通知内容,并设置处理完成的回调Block。
Complications 界面
Complications 是是表盘上除了时间以外的一些功能性的小部件。分为五种:
创建项目时候勾选 Include Complication 就行了。在 Extension 中的ComplicationController.swift
就是具体的配置文件,而且这并不是我们一直使用的 WatchKit 框架,而是一个新的 ClockKit 框架。
通过以下两个方法设定我们所能提供 complication 的日期区间,在这区间之前或之后的数据将不会显示:
1
2optional public func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void)
optional public func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void)
通过以下三个方法取得当前的内容:
1
2
3public func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimelineEntry?) -> Void)
optional public func getTimelineEntriesForComplication(complication: CLKComplication, beforeDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void)
optional public func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: ([CLKComplicationTimelineEntry]?) -> Void)
其中第一个方法很明显,是取得当前的时间。后两个方法是取得之前\之后的时间轴。
而如果要手动刷新数据的话可以用以下方法:
1
2
3
4
5
6static func reloadComplications() {
let server = CLKComplicationServer.sharedInstance()
for complication in server.activeComplications {
server.reloadTimelineForComplication(complication)
}
}
界面元素
基本的元素
WKInterfaceObject 在 WatchKit 中负责界面的元素,但和 UIView 或者 UIView 的子类相比,功能少了很多,只能设置 Hidden、Alpha、Width、Height 等,具体的 UI 设置只能在 Storyboard 中进行。
WKInterfaceButton、WKInterfaceImage、WKInterfaceLabel、WKInterfaceSeparator、WKInterfaceSlider、WKInterfaceSwitch 和 UIKit 中对应的的差不多,直接用就行了。
WKInterfaceDate、WKInterfaceTimer 用于显示时间,毕竟是作用于手表上。WKInterfaceDate 相当于 UILabel + NSDateFormatter,用来展示目前的日期或时间。WKInterfaceTimer 相当于 UILabel + NSDateFormatter + NSTimer,可以作为计时器。
WKInterfaceMap、WKInterfaceMovie 地图和音视频,一目了然。其中 WKInterfaceMovie 本地和网络是都支持的。
WKInterfacePicker
是一个通过表冠来进行交互的滚动列表。其中由 WKPickerItem 组成,可以是文本、图像、或是图文混合。通过三种不同风格来展示:
- List:垂直排列显示的列表
- Stacked:转为一叠卡片通过滚动表冠来一张张显示
- ImageSequence:从图像序列中抽出单个展示
WKInterfaceDevice
相当于 UIDevice。是一个单例类,可以获得当前 Apple Watch 的 screenBounds、screenScale、systemVersion、preferredContentSizeCategory 等信息。
WKInterfaceTable
没有 sections、header、footer、data sources 或者 delegate 什么的。其中每一个 cell 是直接继承于 NSObject,通过 identifier 来区分。
1
2
3public func setRowTypes(rowTypes: [String])
public func setNumberOfRows(numberOfRows: Int, withRowType rowType: String)
public var numberOfRows: Int { get }
通过以上的方法来设置 Table 的行数和样式。并通过public func rowControllerAtIndex(index: Int) -> AnyObject?
来获取具体的 cell 并赋值。
1
2public func insertRowsAtIndexes(rows: NSIndexSet, withRowType rowType: String)
public func removeRowsAtIndexes(rows: NSIndexSet)
通过以上的方法进行添加、删除 cell。
WKInterfaceMenu、WKInterfaceMenuItem
长按手表屏幕,如果当前 WKInterfaceController 中存在上下文菜单的话,就会尝试呼出找这个界面对应的 Context Menu。这个菜单最多可以提供四个按钮,用来针对当前环境向用户征询操作。可以在 StoryBoard 里添加,或者在 WKInterfaceController 通过以下方法添加或者操作:
1
2
3
4public func addMenuItemWithImage(image: UIImage, title: String, action: Selector)
public func addMenuItemWithImageNamed(imageName: String, title: String, action: Selector)
public func addMenuItemWithItemIcon(itemIcon: WKMenuItemIcon, title: String, action: Selector)
public func clearAllMenuItems()
需要注意的是,除了用 StoryBoard 设置和以上方法外,我们无法在 WatchKit 获取 WKInterfaceMenu 和 WKInterfaceMenuItem 的实例或者代理。
WKAlertAction
在 WKInterfaceController 中可以调用public func presentAlertControllerWithTitle(title: String?, message: String?, preferredStyle: WKAlertControllerStyle, actions: [WKAlertAction])
方法在当前界面弹出一个 alert view。根据你设定的 preferredStyle 参数来显示出不同的样式。其中的 actions 参数是包含 WKAlertAction 实例的数组,通过用户点击相应的 button 来执行它自身提供的 block。要注意的是,你需要 dismiss 这些 alert view。
WKInterfaceGroup
Watch App 的视图开发不能使用代码,必须 StoryBoard。采取的布局方式和 iOS App 完全不同。不能使用 autoLayout 或者坐标,只能使用相对布局。默认是以行为单位垂直布局。如果要水平布局就需要以 WKInterfaceObject 重叠布局。相当于 Android 的 layout 布局控件。
还可以设置背景、CornerRadius、ContentInset 属性。
数据通信
target membership
这个方法用于使一个文件在多个 target 实现重用,一般用于 Model 中。
WatchConnectivity
WatchConnectivity 的主体是 WCSession,它是个单例类,直接使用public class func defaultSession() -> WCSession
单例对象就可以了。首先我们需要用 WCSession 的public class func isSupported() -> Bool
方法检查设备上是否支持 WatchConnectivity ,iPad 是不支持的。 要使用 WCSession,先要激活它。指定一个对象遵从 WCSessionDelegate,然后调用public func activateSession()
方法激活。
数据发送分为后台发送和即时消息两类。当 iOS app 和 watch app 都在前台的时候,我们可以通过以下方法来在两者之间发送消息:
1
2public func sendMessage(message: [String : AnyObject], replyHandler: (([String : AnyObject]) -> Void)?, errorHandler: ((NSError) -> Void)?)
public func sendMessageData(data: NSData, replyHandler: ((NSData) -> Void)?, errorHandler: ((NSError) -> Void)?)
其中 message 或者 data 为传递过去的数据,replyHandler 中的为返回回来的数据。对应的 app 通过 WCSessionDelegate 的以下方法来接收数据:
1
2
3
4optional public func session(session: WCSession, didReceiveMessage message: [String : AnyObject])
optional public func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
optional public func session(session: WCSession, didReceiveMessageData messageData: NSData)
optional public func session(session: WCSession, didReceiveMessageData messageData: NSData, replyHandler: (NSData) -> Void)
后台通讯有三种方式:
- 通过 Application Context:
public func updateApplicationContext(applicationContext: [String : AnyObject]) throws
以字典数据来传输到对应的 app。不会即刻发送,但会在对应的 app 唤醒的时候发送。新传递的数据会覆盖原来的内容。使用 WCSessionDelegate 的optional public func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject])
方法来接收数据。 - 通过 User Info:
public func transferUserInfo(userInfo: [String : AnyObject]) -> WCSessionUserInfoTransfer
同样是传递一个字典,但与 Application Context 不同的是可以使多次内容形成队列进行传送。使用 WCSessionDelegate 的optional public func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject])
方法来接收数据。 - 传送文件:
public func transferFile(file: NSURL, metadata: [String : AnyObject]?) -> WCSessionFileTransfer
用于传输文件,比如通过后台来传送图片。使用 WCSessionDelegate 的optional public func session(session: WCSession, didReceiveFile file: WCSessionFile)
方法来接收数据。
其它
动画
帧动画
通过为 WKInterfaceImage 设置包含多个 image 的图像,通过图片名称序列号实现动画,或者是通过计时器定时替换图像可实现帧动画效果。
1
2
3public func startAnimating()
public func startAnimatingWithImagesInRange(imageRange: NSRange, duration: NSTimeInterval, repeatCount: Int)
public func stopAnimating()
基本动画
在 WKInterfaceController 中调用public func animateWithDuration(duration: NSTimeInterval, animations: () -> Void)
方法实现,用法和public class func animateWithDuration(duration: NSTimeInterval, animations: () -> Void)
一样。
多媒体
视频播放
1
2public func presentMediaPlayerControllerWithURL(URL: NSURL, options: [NSObject : AnyObject]?, completion: (Bool, NSTimeInterval, NSError?) -> Void)
public func dismissMediaPlayerController()
通过以上方法和 WKInterfaceMovie 其实差不多,都是跳转到一个新的 WKInterfaceController 实现的。
音频录制
1
2public func presentAudioRecorderControllerWithOutputURL(URL: NSURL, preset: WKAudioRecorderPreset, options: [NSObject : AnyObject]?, completion: (Bool, NSError?) -> Void)
public func dismissAudioRecorderController()
通过以上方法实现,但只支持只支持 .wav、 .mp4、 .m4a 三种格式。
音频播放
Watch App 需要借助蓝牙设备才能控制播放音乐
语音输入
1
2
3public func presentTextInputControllerWithSuggestions(suggestions: [String]?, allowedInputMode inputMode: WKTextInputMode, completion: ([AnyObject]?) -> Void)
public func presentTextInputControllerWithSuggestionsForLanguage(suggestionsHandler: ((String) -> [AnyObject]?)?, allowedInputMode inputMode: WKTextInputMode, completion: ([AnyObject]?) -> Void)
public func dismissTextInputController()
openSystemURL
通过 WKExtension 的public func openSystemURL(url: NSURL)
方法可以实现发短信打电话功能。