本文是投稿文章,作者Swift_波
開始
打開 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:
@IBOutlet var originLabel: WKInterfaceLabel! @IBOutlet var destinationLabel: WKInterfaceLabel! @IBOutlet var boardingPassImage: WKInterfaceImage!
這裡僅僅為剛才創建的圖片控件和兩個標簽增加連線。只要一瞬間你就能連接他們。
在連線下面增加如下代碼:
var flight: Flight? { didSet { if let flight = flight { originLabel.setText(flight.origin) destinationLabel.setText(flight.destination) } } }
這又是我們的老朋友 flight 和它的屬性觀察器! 雖然你知道即將發生什麼,但是讓我們來回顧一下,你添加了一個可選的 Flight 類型屬性,包括一個屬性觀察器。當觀察器觸發,嘗試解包 flight,當解包成功使用 flight 來配置兩個標簽。
現在僅僅需要在控制器第一次打開的時候設置 flight 屬性。添加如下代碼到 BoardingPassInterfaceController:
override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) if let flight = context as? 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:)。替換這句代碼:
let controllers = ["Flight", "CheckIn"]
為下面這句代碼:
let controllers = flight.checkedIn ? ["Flight", "BoardingPass"] : ["Flight", "CheckIn"]
這裡僅僅判斷用戶是否登記過選中的航班,如果登記過就顯示航班詳情和登機牌界面。如果沒有,代替顯示航班詳情和登記界面。
編譯運行。點擊第一個航班,往左清掃,點擊 Check In。再次點擊相同的航班,往左清掃,你會看到登機牌界面,顯示不確定進度指示器:
是時候深入學習新的 Watch Connectivity 框架並且使用它請求真實的登機牌數據。
請求登機牌
打開 BoardingPassInterfaceController.swift 導入 Watch Connectivity 框架:
import
WatchConnecivity
下一步,在上面定義的 flight 的下面添加如下屬性:
var session: WCSession? { didSet { if let session = session { session.delegate = self session.activateSession() } } }
這裡添加一個新的類型為 WCSession 的可選屬性。在手表和手機兩個設備間的所有連接操作都是由它處理的;你自己並不需要實例化這個類,而是使用框架提供的單例。你已經添加屬性觀察器了,當它觸發了,嘗試解包 session。當解包成功設置 session 的代理,之後激活它。
即使你不實現類的任何代理方法,你任然需要在激活前設置 session 的代理,不然情況會變得未知。
Xcode 可能會警告 BoardingPassInterfaceController 沒有遵循 WCSessionDelegate 協議,所以在 BoardingPassInterfaceController.swift 的底部添加如下空的擴展:
extension BoardingPassInterfaceController: WCSessionDelegate {
}
下一步,往 BoardingPassInterfaceController 添加如下幫助方法:
private func showBoardingPass() { boardingPassImage.stopAnimating() boardingPassImage.setWidth(120) boardingPassImage.setHeight(120) boardingPassImage.setImage(flight?.boardingPass) }
它會在兩處調用 - 如果航班已經有登機牌了在 flight 的屬性觀察器中調用,還有另外一處是你發給你的 iPhone 的消息回調。實現非常簡單-停止圖片動畫,增加圖片大小,之後設置顯示到登機牌的圖片。
首先更新屬性觀察器,往 flight 屬性觀察器中的 if 代碼塊的底部增加如下代碼
if let _ = flight.boardingPass { showBoardingPass() }
只有當 flight 存在一個登機牌的時候調用 showBoardingPass() 方法。
最後一部分代碼是往 iPhone 發送請求。在 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,會繼續進入發送消息的模塊。在嘗試與配對的手機做任何連接前你應該經常檢查是否支持 Watch Connectivity。
設置 session 值為默認的 WCSession 單例。這會相應的觸發屬性觀察器,在激活 session 之前 設置它的代理。
往配對的 iPhone app 發送消息。一個包括航班信息的字典被轉發到 iPhone app,並且提供回調和錯誤處理。
iPhone app 處理接收的消息然後返回數據給手表端。手表端從返回數據中提取登機牌的圖片信息來創建一個 UIImage 對象。
如果操作成功了,設置 UIImage 為航班登機牌的圖片,之後回到主線程調用 showBoardingPass() 來顯示給用戶。回調和錯誤處理是在後台線程中執行,所以如果你需要像現在這樣更新界面,確保是在主線程中更新。
如果消息發送失敗簡單的打印錯誤到命令行。
這些是手表 app 端處理。現在需要相應的更新 iPhone app 端了。
回應請求
首先,導入 Watch Connectivity 框架:
import WatchConnectivity
之後,在 window 下面添加如下代碼:
var session: WCSession? { didSet { if let session = 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) { if let reference = message["reference"] as? String, boardingPass = QRCode(reference) { replyHandler(["boardingPassData": boardingPass.PNGData]) } } }
這裡實現了 WCSessionDelegate 方法負責接收消息。從手表傳過來的字典中提取航班信息,之後用 Alexander Schuch 寫的牛逼的 QRCode 庫來生成二維碼,如果生成成功,調用回調函數,傳遞圖片信息到手表 app。
最後,設置 session。添加如下代碼到application(_:didFinishLaunchingWithOptions:) 方法中:
if WCSession.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 的技巧。
系列閱讀:
watchOS 2教程(一):開始吧
watchOS 2 教程(二):列表
watchOS 2 教程(三):動畫
watchOS 2 教程(四): Watch Connectivity(本篇)