原文: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 放到新的控制器中,確保它與你剛才粘貼的組同級,而不是子節(jié)點:
image 控件有兩個目的;最初它顯示動畫圖片序列來告訴用戶發(fā)生什么事情鸭限,之后當手表從手機獲取到登機牌蜕径,image 會顯示它。
下載壓縮文件,解壓縮文件败京,然后拖動文件夾到 Watch\Assets.xcassets 目錄中兜喻。
確保拖動的是文件夾而不是其中的文件。這會在 asset catalog 中創(chuàng)建一個新的叫 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 不能實時預覽動畫圖片-但是請相信我蒂教,運行時就沒這問題了巍举。
設計完登機牌界面。現(xiàn)在創(chuàng)建 WKInterfaceController 的子類來做后續(xù)的工作凝垛。
創(chuàng)建控制器
在項目導航中右擊 Watch Extension 組懊悯,選擇 New File...。當對話框彈出來后選擇 watchOS\Source\WatchKit Class 然后點擊 Next梦皮。命名新的類為 BoardingPassInterfaceController,確保它是 WKInterfaceController 的子類并且語言設置為 Swift:
點擊 Next,之后 Create炭分。
當新的文件在代碼編輯器中打開了,刪除三個空的方法剑肯,只剩下重要代碼和類定義捧毛。
之后,在類的頂部添加如下 outlets:
@IBOutlet var originLabel: WKInterfaceLabel!
@IBOutlet var destinationLabel: WKInterfaceLabel!
@IBOutlet var boardingPassImage: WKInterfaceImage!
這里僅僅為剛才創(chuàng)建的圖片控件和兩個標簽增加連線让网。只要一瞬間你就能連接他們呀忧。
在連線下面增加如下代碼:
var flight: Flight? {
didSet {
if let flight = flight {
originLabel.setText(flight.origin)
destinationLabel.setText(flight.destination)
}
}
}
這又是我們的老朋友 flight 和它的屬性觀察器! 雖然你知道即將發(fā)生什么寂祥,但是讓我們來回顧一下荐虐,你添加了一個可選的 Flight 類型屬性七兜,包括一個屬性觀察器丸凭。當觀察器觸發(fā),嘗試解包 flight腕铸,當解包成功使用 flight 來配置兩個標簽惜犀。
現(xiàn)在僅僅需要在控制器第一次打開的時候設置 flight 屬性。添加如下代碼到 BoardingPassInterfaceController:
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if let flight = context as? Flight { self.flight = flight }
}
另一個老朋友狠裹;嘗試解包轉換 context 為 Flight 對象虽界!如果轉換成功使用它來設置 self.flight,相應的觸發(fā)屬性觀察器來配置界面。
我保證這就是練習的樣板代碼涛菠。:]
現(xiàn)在莉御,打開 Watch\Interface.storyboard 選擇登機牌控制器。在 Identity Inspector 中俗冻,修改 Custom Class\Class 為 BoardingPassInterfaceController:
在文檔大綱中右擊 BoardingPass 打開 outlets 和 actions 彈出框礁叔。連接 boardingPassImage 到 image:
最后,連接 destinationLabel 到 文本為 SFO 的標簽迄薄,連接 originLabel 到文字是 MAN 的標簽琅关。
當完成這些操作,是時候更新 ScheduleInterfaceController 代碼讥蔽, 一旦用戶登記了就打開登機牌界面涣易。
打開登機牌界面
打開 ScheduleInterfaceController.swift 找到 table(_:didSelectRowAtIndex:)画机。替換這句代碼:
let controllers = ["Flight", "CheckIn"]
為下面這句代碼:
let controllers = flight.checkedIn ? ["Flight", "BoardingPass"] : ["Flight", "CheckIn"]
這里僅僅判斷用戶是否登記過選中的航班,如果登記過就顯示航班詳情和登機牌界面新症。如果沒有步氏,代替顯示航班詳情和登記界面。
編譯運行徒爹。點擊第一個航班戳护,往左清掃,點擊 Check In瀑焦。再次點擊相同的航班腌且,往左清掃,你會看到登機牌界面榛瓮,顯示不確定進度指示器:
是時候深入學習新的 Watch Connectivity 框架并且使用它請求真實的登機牌數(shù)據(jù)铺董。
請求登機牌
打開 BoardingPassInterfaceController.swift 導入 Watch Connectivity 框架:
import WatchConnecivity
下一步,在上面定義的 flight 的下面添加如下屬性:
var session: WCSession? {
didSet {
if let session = session {
session.delegate = self
session.activateSession()
}
}
}
這里添加一個新的類型為 WCSession 的可選屬性禀晓。在手表和手機兩個設備間的所有連接操作都是由它處理的精续;你自己并不需要實例化這個類,而是使用框架提供的單例粹懒。你已經(jīng)添加屬性觀察器了重付,當它觸發(fā)了,嘗試解包 session凫乖。當解包成功設置 session 的代理确垫,之后激活它。
即使你不實現(xiàn)類的任何代理方法帽芽,你任然需要在激活前設置 session 的代理删掀,不然情況會變得未知。
Xcode 可能會警告 BoardingPassInterfaceController 沒有遵循 WCSessionDelegate 協(xié)議导街,所以在 BoardingPassInterfaceController.swift 的底部添加如下空的擴展:
extension BoardingPassInterfaceController: WCSessionDelegate {
}
下一步披泪,往 BoardingPassInterfaceController 添加如下幫助方法:
private func showBoardingPass() {
boardingPassImage.stopAnimating()
boardingPassImage.setWidth(120)
boardingPassImage.setHeight(120)
boardingPassImage.setImage(flight?.boardingPass)
}
它會在兩處調用 - 如果航班已經(jīng)有登機牌了在 flight 的屬性觀察器中調用,還有另外一處是你發(fā)給你的 iPhone 的消息回調搬瑰。實現(xiàn)非常簡單-停止圖片動畫款票,增加圖片大小,之后設置顯示到登機牌的圖片泽论。
首先更新屬性觀察器艾少,往 flight 屬性觀察器中的 if 代碼塊的底部增加如下代碼
if let _ = flight.boardingPass {
showBoardingPass()
}
只有當 flight 存在一個登機牌的時候調用 showBoardingPass() 方法。
最后一部分代碼是往 iPhone 發(fā)送請求佩厚。在 awakeWithContext(_:) 代碼的下面添加如下代碼:
override func didAppear() {
super.didAppear()
// 1
if let flight = flight where flight.boardingPass == nil && WCSession.isSupported() {
// 2
session = WCSession.defaultSession()
// 3
session!.sendMessage(["reference": flight.reference], replyHandler: { (response) -> Void in
// 4
if let boardingPassData = response["boardingPassData"] as? NSData, boardingPass = UIImage(data: boardingPassData) {
// 5
flight.boardingPass = boardingPass
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.showBoardingPass()
})
}
}, errorHandler: { (error) -> Void in
// 6
print(error)
})
}
}
下面一步步講解以上代碼怎么回事:
- 假如存在有效航班姆钉,沒有登機牌,并且支持 Watch Connecivity,會繼續(xù)進入發(fā)送消息的模塊潮瓶。在嘗試與配對的手機做任何連接前你應該經(jīng)常檢查是否支持 Watch Connectivity陶冷。
- 設置 session 值為默認的 WCSession 單例。這會相應的觸發(fā)屬性觀察器毯辅,在激活 session 之前 設置它的代理埂伦。
- 往配對的 iPhone app 發(fā)送消息。一個包括航班信息的字典被轉發(fā)到 iPhone app思恐,并且提供回調和錯誤處理沾谜。
- iPhone app 處理接收的消息然后返回數(shù)據(jù)給手表端。手表端從返回數(shù)據(jù)中提取登機牌的圖片信息來創(chuàng)建一個 UIImage 對象胀莹。
- 如果操作成功了基跑,設置 UIImage 為航班登機牌的圖片,之后回到主線程調用 showBoardingPass() 來顯示給用戶描焰∠狈瘢回調和錯誤處理是在后臺線程中執(zhí)行,所以如果你需要像現(xiàn)在這樣更新界面荆秦,確保是在主線程中更新篱竭。
- 如果消息發(fā)送失敗簡單的打印錯誤到命令行。
這些是手表 app 端處理〔匠瘢現(xiàn)在需要相應的更新 iPhone app 端了掺逼。
回應請求
首先,導入 Watch Connectivity 框架:
import WatchConnectivity
之后瓤介,在 window 下面添加如下代碼:
var session: WCSession? {
didSet {
if let session = session {
session.delegate = self
session.activateSession()
}
}
}
操作與 BoardingInterfaceController 中命名一樣吕喘。簡單的一個類型 WCSession 的可選屬性,包括屬性觀察器惑朦,當觸發(fā)了觀察器兽泄,嘗試解包 session漓概。如果解包成功設置 session 的代理并且激活它漾月。
下一步,在 AppDelegate.swift 文件中添加如下擴展:
extension AppDelegate: WCSessionDelegate {
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
if let reference = message["reference"] as? String, boardingPass = QRCode(reference) {
replyHandler(["boardingPassData": boardingPass.PNGData])
}
}
}
這里實現(xiàn)了 WCSessionDelegate 方法負責接收消息胃珍。從手表傳過來的字典中提取航班信息梁肿,之后用 Alexander Schuch 寫的牛逼的 QRCode 庫來生成二維碼,如果生成成功,調用回調函數(shù)觅彰,傳遞圖片信息到手表 app吩蔑。
最后,設置 session填抬。添加如下代碼到application(_:didFinishLaunchingWithOptions:) 方法中:
if WCSession.isSupported() {
session = WCSession.defaultSession()
}
這里確保支持 Watch Connectivity 烛芬,之后設置 session 為框架提供的默認的 WCSession 單例。
你現(xiàn)在能夠與 iPhone app 進行雙向對話了。
編譯運行赘娄。按照如上步驟來登記航班然后查看登機牌仆潮。這次登機牌應該等段時間之后才能出現(xiàn):
祝賀!你已經(jīng)完成了使用 Watch Connectivity 向 iPhone app 請求登機牌遣臼;棒極了性置。
下一步做什么?
這是系列教程的完整示例項目
在本教程中揍堰,你學習如何在 watch app 和配對的 iPhone app 間發(fā)送實時消息鹏浅,如何在兩個設備間傳遞圖片信息。
如果你喜歡這個系列想要更多的學習關于 watchOS 2 開發(fā)知識屏歹,來看看我們的 watchOS 2 by Tutorials 書隐砸,它會教你很多開發(fā) watchOS 2 apps 的技巧。