watchOS 2 教程(四): Watch Connectivity

原文:watchOS 2 Tutorial Part 3: Animation

欢迎回到 watchOS 2 系列教程!

开始

打开 Watch\Interface.storyboard,从对象库拖动一个 Interface Controller 到 storyboard 画板中。选中控制器,打开属性检查器做如下修改:

设置 Identifier 为 BoardingPass;

设置 Insets 为 Custom;

设置 Top inset 为 6。

因为这个界面非常像 check-in 界面,设计界面有时候有些重复的工作,这时候你要灵活一点。

在文档大纲中点开 CheckIn Scene,选择那个包括起点和终点标签的组,之后 Edit\Copy:

点击 storyboard 中那个新的控制器的任何地方,选择 Edit\Paste。这个只在直接往控制器里面粘贴时候有用,而往文档大纲中粘贴没有用,但是我也不知道为什么。

新的控制器应该是这样:

下一步,从对象库拖动一个 Image 放到新的控制器中,确保它与你刚才粘贴的组同级,而不是子节点:

image 控件有两个目的;最初它显示动画图片序列来告诉用户发生什么事情,之后当手表从手机获取到登机牌,image 会显示它。

下载压缩文件,解压缩文件,然后拖动文件夹到 Watch\Assets.xcassets 目录中。

确保拖动的是文件夹而不是其中的文件。这会在 asset catalog 中创建一个新的叫 Activity 的组,它包含一些图片集合:

当你正在从配对的手机中请求登机牌的时候,用这个图片序列显示不确定进度指示器。

重新打开 Watch\Interface.storyboard 然后选择之前那个 image。使用你的老朋友属性检查器做如下修改:

设置 Image 为 Activity。自动补全有可能会建议例如 Activity1,所以确保你输入的是 Activity;

设置 Animate 为 Yes;

设置 Duration 为1;

选中 Animate on Load;

设置 Horizontal alignment 为 Center;

设置 Vertical alignment 为 Center;

设置 Width 为 Fixed,值为66;

设置 Height 为 Fixed,值为66;

当修改完成,你的属性检查器应该像这样:

控制器应该像这样:

可以看到 Image 的预览图片是一个又大又模糊的问题标记,不要担心;因为没有叫 Activity 的图片,所以 IB 不能实时预览动画图片-但是请相信我,运行时就没这问题了。

设计完登机牌界面。现在创建 WKInterfaceController 的子类来做后续的工作。

创建控制器

在项目导航中右击 Watch Extension 组,选择 New File...。当对话框弹出来后选择 watchOS\Source\WatchKit Class 然后点击 Next。命名新的类为 BoardingPassInterfaceController,确保它是 WKInterfaceController 的子类并且语言设置为 Swift:

点击 Next,之后 Create。

当新的文件在代码编辑器中打开了,删除三个空的方法,只剩下重要代码和类定义。

之后,在类的顶部添加如下 outlets:

@IBOutletvar originLabel: WKInterfaceLabel!@IBOutletvar destinationLabel: WKInterfaceLabel!@IBOutletvar boardingPassImage: WKInterfaceImage!

这里仅仅为刚才创建的图片控件和两个标签增加连线。只要一瞬间你就能连接他们。

在连线下面增加如下代码:

varflight: Flight? {  didSet {ifletflight = flight {      originLabel.setText(flight.origin)      destinationLabel.setText(flight.destination)    }  }}

这又是我们的老朋友 flight 和它的属性观察器! 虽然你知道即将发生什么,但是让我们来回顾一下,你添加了一个可选的 Flight 类型属性,包括一个属性观察器。当观察器触发,尝试解包 flight,当解包成功使用 flight 来配置两个标签。

现在仅仅需要在控制器第一次打开的时候设置  flight 属性。添加如下代码到 BoardingPassInterfaceController:

override func awakeWithContext(context: AnyObject?) {super.awakeWithContext(context)ifletflight = contextas? Flight { self.flight = flight }}

另一个老朋友;尝试解包转换 context 为 Flight 对象!如果转换成功使用它来设置 self.flight,相应的触发属性观察器来配置界面。

我保证这就是练习的样板代码。:]

现在,打开 Watch\Interface.storyboard 选择登机牌控制器。在 Identity Inspector 中,修改 Custom Class\Class 为 BoardingPassInterfaceController:

在文档大纲中右击 BoardingPass 打开 outlets 和 actions 弹出框。连接 boardingPassImage 到 image:

最后,连接 destinationLabel 到 文本为 SFO 的标签,连接 originLabel 到文字是 MAN 的标签。

当完成这些操作,是时候更新 ScheduleInterfaceController 代码, 一旦用户登记了就打开登机牌界面。

打开登机牌界面

打开 ScheduleInterfaceController.swift 找到 table(_:didSelectRowAtIndex:)。替换这句代码:

letcontrollers = ["Flight","CheckIn"]

为下面这句代码:

letcontrollers = flight.checkedIn ? ["Flight","BoardingPass"] : ["Flight","CheckIn"]

这里仅仅判断用户是否登记过选中的航班,如果登记过就显示航班详情和登机牌界面。如果没有,代替显示航班详情和登记界面。

编译运行。点击第一个航班,往左清扫,点击 Check In。再次点击相同的航班,往左清扫,你会看到登机牌界面,显示不确定进度指示器:

是时候深入学习新的 Watch Connectivity 框架并且使用它请求真实的登机牌数据。

请求登机牌

打开 BoardingPassInterfaceController.swift  导入 Watch Connectivity 框架:

importWatchConnecivity

下一步,在上面定义的 flight 的下面添加如下属性:

varsession: WCSession? {  didSet {ifletsession = session {      session.delegate = self      session.activateSession()    }  }}

这里添加一个新的类型为 WCSession 的可选属性。在手表和手机两个设备间的所有连接操作都是由它处理的;你自己并不需要实例化这个类,而是使用框架提供的单例。你已经添加属性观察器了,当它触发了,尝试解包 session。当解包成功设置 session 的代理,之后激活它。

即使你不实现类的任何代理方法,你任然需要在激活前设置 session 的代理,不然情况会变得未知。

Xcode 可能会警告 BoardingPassInterfaceController 没有遵循 WCSessionDelegate 协议,所以在 BoardingPassInterfaceController.swift 的底部添加如下空的扩展:

extensionBoardingPassInterfaceController:WCSessionDelegate{ }

下一步,往 BoardingPassInterfaceController 添加如下帮助方法:

privatefuncshowBoardingPass(){  boardingPassImage.stopAnimating()  boardingPassImage.setWidth(120)  boardingPassImage.setHeight(120)  boardingPassImage.setImage(flight?.boardingPass)}

它会在两处调用 - 如果航班已经有登机牌了在 flight 的属性观察器中调用,还有另外一处是你发给你的 iPhone 的消息回调。实现非常简单-停止图片动画,增加图片大小,之后设置显示到登机牌的图片。

首先更新属性观察器,往 flight 属性观察器中的 if 代码块的底部增加如下代码

iflet_ = flight.boardingPass {  showBoardingPass()}

只有当 flight 存在一个登机牌的时候调用 showBoardingPass() 方法。

最后一部分代码是往 iPhone 发送请求。在 awakeWithContext(_:) 代码的下面添加如下代码:

override func didAppear() {super.didAppear()// 1iflet flight = flight where flight.boardingPass ==nil&& WCSession.isSupported() {// 2session = WCSession.defaultSession()// 3session!.sendMessage(["reference": flight.reference], replyHandler: { (response) -> Voidin// 4iflet boardingPassData = response["boardingPassData"] as?NSData, boardingPass =UIImage(data: boardingPassData) {// 5flight.boardingPass = boardingPassdispatch_async(dispatch_get_main_queue(), { () -> Voidinself.showBoardingPass()        })      }    }, errorHandler: { (error) -> Voidin// 6print(error)    })  }}

下面一步步讲解以上代码怎么回事:

假如存在有效航班,没有登机牌,并且支持 Watch Connecivity,会继续进入发送消息的模块。在尝试与配对的手机做任何连接前你应该经常检查是否支持 Watch Connectivity。

设置 session 值为默认的 WCSession 单例。这会相应的触发属性观察器,在激活 session 之前 设置它的代理。

往配对的 iPhone app 发送消息。一个包括航班信息的字典被转发到 iPhone app,并且提供回调和错误处理。

iPhone app 处理接收的消息然后返回数据给手表端。手表端从返回数据中提取登机牌的图片信息来创建一个 UIImage 对象。

如果操作成功了,设置 UIImage 为航班登机牌的图片,之后回到主线程调用 showBoardingPass() 来显示给用户。回调和错误处理是在后台线程中执行,所以如果你需要像现在这样更新界面,确保是在主线程中更新。

如果消息发送失败简单的打印错误到命令行。

这些是手表 app 端处理。现在需要相应的更新 iPhone app 端了。

回应请求

首先,导入 Watch Connectivity 框架:

importWatchConnectivity

之后,在 window 下面添加如下代码:

varsession: WCSession? {  didSet {ifletsession = session {      session.delegate = self      session.activateSession()    }  }}

操作与 BoardingInterfaceController 中命名一样。简单的一个类型 WCSession 的可选属性,包括属性观察器,当触发了观察器,尝试解包 session。如果解包成功设置 session 的代理并且激活它。

下一步,在 AppDelegate.swift 文件中添加如下扩展:

extension AppDelegate: WCSessionDelegate {  func session(session: WCSession, didReceiveMessage message: [String: AnyObject],replyHandler: ([String: AnyObject]) -> Void) {ifletreference = message["reference"]as?String, boardingPass = QRCode(reference) {      replyHandler(["boardingPassData": boardingPass.PNGData])    }  } }

这里实现了 WCSessionDelegate 方法负责接收消息。从手表传过来的字典中提取航班信息,之后用 Alexander Schuch 写的牛逼的 QRCode 库来生成二维码,如果生成成功,调用回调函数,传递图片信息到手表 app。

最后,设置 session。添加如下代码到application(_:didFinishLaunchingWithOptions:) 方法中:

ifWCSession.isSupported() {  session = WCSession.defaultSession()}

这里确保支持 Watch Connectivity ,之后设置 session 为框架提供的默认的 WCSession 单例。

你现在能够与 iPhone app 进行双向对话了。

编译运行。按照如上步骤来登记航班然后查看登机牌。这次登机牌应该等段时间之后才能出现:

祝贺!你已经完成了使用 Watch Connectivity 向 iPhone app 请求登机牌;棒极了。

下一步做什么?

这是系列教程的完整示例项目

在本教程中,你学习如何在 watch app 和配对的 iPhone app 间发送实时消息,如何在两个设备间传递图片信息。

如果你喜欢这个系列想要更多的学习关于 watchOS 2 开发知识,来看看我们的 watchOS 2 by Tutorials 书,它会教你很多开发 watchOS 2 apps 的技巧。

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

推荐阅读更多精彩内容