Apple WatchKit 初探

Apple WatchKit 初探

2014/11/20 | 分類: IOS開發 | 0 條評論 | 標籤: APPLEAPPLE WATCHios

分享到:1swift

原文出處: 王巍的博客 (@onevcat)   歡迎分享原創到伯樂頭條框架

隨着今天凌晨 Apple 發佈了初版的 Watch Kit 的 API,對於開發者來講,這款新設備的一些更詳細的信息也算是逐漸浮出水面。能夠說初版的 WatchKit 開放的功能整體仍是使人滿意的。Apple 在承諾逐漸開放的方向上繼續前進。原本在 WWDC 以後預期 Today Widget 會是各種新穎 app 的舞臺以及對 iOS 功能的極大擴展,可是隨着像 Launcher 和 PCalc 這些創意型的 Today Widget 接連被下架事件,讓開發者也不得不下調對 WatchKit 的預期。可是至少從如今的資料來看,WatchKit 是容許進行復雜交互以及完成一些獨立功能的。雖然須要依託於 iPhone app,可是至少可以發揮的舞臺和空間要比我原先想象的大很多。less

固然,由於設備自己的不管是電量仍是運算能力的限制,在進行 Watch app 開發的時候也仍是有不少掣肘。如今 Watch app 僅僅只是做爲視圖顯示和回傳用戶交互的存在,可是考慮到這是這款設備的初版 SDK,另外 Apple 也有承諾以後會容許真正運行在 Watch 上的 app 的出現,Apple Watch 和 WatchKit 的將來仍是很值得期待的。ide

廢話再也不多,咱們來簡單看看 WatchKit 的一些基本信息吧。oop

咱們能作什麼

Watch app 架構

首先須要明確的是,在 iOS 系統上,app 本體是核心。全部的運行實體都是依託在本體上的:在 iOS 8 以前這是毋庸置疑的,而在 iOS 8 中添加的各類 Extension 也必須隨¥同 app 本體捆綁,做爲 app 的功能的補充。Watch app 雖然也相似於此,咱們要針對 Apple Watch 進行開發,首先仍是須要創建一個傳統的 iOS app,而後在其中添加 Watch App 的 target。在添加以後,會發現項目中多出兩個 target:其中一個是 WatchKit 的擴展,另外一個是 Watch App。在項目相應的 group 下能夠看到,WatchKit Extension 中含有代碼 (InterfaceController.h/m 等),而 Watch App 裏只包含了 Interface.storyboard。不過暫時看來的好消息是 Apple 並無像對 iPhone Extension 那樣明確要求針對 Watch 開發的 app 必須仍是以 iOS app 爲核心。也就是說,將 iOS app 空殼化而專一提供 Watch 的 UI 和體驗也許是被容許的。

在應用安裝時,負責邏輯部分的 WatchKit Extension 將隨 iOS app 的主 target 被一同安裝到 iPhone 中,而負責界面部分的 WatchKit App 將會在主程序安裝後由 iPhone 檢測有沒有配對的 Apple Watch,並提示安裝到 Apple Watch 中。因此在實際使用時,全部的運算、邏輯以及控制實際上都是在 iPhone 中完成的。在須要界面刷新時,由 iPhone 向 Watch 發送指令進行描畫並在手錶盤面上顯示。反過來,用戶觸摸手錶交互時的信息也由手錶傳回給 iPhone 並進行處理。而這個過程 WatchKit 會在幕後爲咱們完成,並不須要開發者操心。咱們須要知道的就是,原則上來講,咱們應該將界面相關的內容放在 Watch App 的 target 中,而將全部代碼邏輯等放到 Extension 裏。

在手錶上點擊 app 圖標運行 Watch App 時,手錶將會負責喚醒手機上的 WatchKit Extension。而 WatchKit Extension 和 iOS app 之間的數據交互需求則由 App Groups 來完成,這和 Today Widget 以及其餘一些 Extension 是同樣的。若是你尚未了解過相關內容,能夠參看我以前寫過的一篇 Today Extension 的教程

主要類

WKInterfaceController 和生命週期

WKInterfaceController 是 WatchKit 中的 UIViewController 同樣的存在,也會是開發 Watch App 時花時間最多的類。每一個 WKInterfaceController 或者其子類應該對應手錶上的一個整屏內容。可是須要記住整個 WatchKit 是獨立於 UIKit 而存在的,WKInterfaceController 是一個直接繼承自 NSObject 的類,並無像 UIKit 中UIResponser 那樣的對用戶交互的響應功能和完備的回調。

不只在功能上相對 UIViewController 簡單不少,在生命週期上也進行了大幅簡化。每一個WKInterfaceController 對象必然會被調用的生命週期方法有三個,分別是該對象被初始化時的 -initWithContext:,將要呈現時的 -willActivate 以及呈現結束後的 -didDeactivate。一樣類比 UIViewController 的話,能夠將它們理解爲分別對應 -viewDidLoadviewWillAppear: 以及 -viewDidDisappear:。雖然看方法名和實際使用上可能你會認爲 -initWithContext: 應該對應 UIViewController 的 init 或者initWithCoder: 這樣的方法,可是事實上在 -initWithContext: 時WKInterfaceController 中的「視圖元素」 (請注意這裏我加上了引號,由於它們不是真正的視圖,稍後會再說明) 都已經初始化完畢可用,這其實和 -viewDidLoad 中的行爲更加類似。

咱們通常在 -initWithContext: 和 -willActivate 中配置「視圖元素」的屬性,在 -didDeactivate 中停用像是 NSTimer 之類的會 hold 住 self 的對象。須要特別注意的是,在 -didDeactivate 中對「視圖元素」屬性進行設置是無效的,由於當前的WKInterfaceController 已經非活躍。

WKInterfaceObject 及其子類

WKInterfaceObject 負責具體的界面元素設置,包括像是WKInterfaceButtonWKInterfaceLabel 或 WKInterfaceImage 這類物件,也就是咱們上面所提到的「視圖元素」。可能一開始會產生錯覺,以爲 WKInterfaceObject 應該對應UIView,但其實上並不是如此。WKInterfaceObject 只是 WatchKit 的實際的 View 的一個在 Watch Extension 端的代理,而非 View 自己。Watch App 中實際展示和渲染在屏幕上的 view 對於代碼來講是非直接可見的,咱們只能在 Extension target 中經過對應的代理對象對屬性進行設置,而後在每一個 run loop 須要刷新 UI 時由 WatchKit 將新的屬性值從手機中傳遞給手錶中的 Watch App 並進行界面刷新。

反過來,手錶中的實際的 view 想要將用戶交互事件傳遞給 iPhone 也須要經過WKInterfaceObject 代理進行。每一個可交互的 WKInterfaceObject 子類都對應了一個 action,好比 button 對應點擊事件,switch 對應開或者關的狀態,slider 對應一個浮點數值代表選取值等等。關聯這些事件也很簡單,直接從 StoryBoard 文件中 Ctrl 拖拽到實現中就能生成對應的事件了。雖然 UI 資源文件和代碼實現是在不一樣的 target 中的,可是在 Xcode 中的協做已然完美無缺。

Watch App 採起的佈局方式和 iOS app 徹底不一樣。你沒法自由指定某個視圖的具體座標,固然也不能使用像 AutoLayout 或者 Size Classes 這樣的靈活的界面佈局方案。WatchKit 提供的佈局可能性和靈活性相對較小,你只能在以「行」爲基本單位的同時經過 group 來在行內進行「列」佈局。這帶來了相對簡單的佈局實現,固然,同時也是對界面交互的設計的一種挑戰。

另外值得一提的是,隨着 WatchKit 的出現及其開發方式的轉變,代碼寫 UI 仍是使用 StoryBoard 這個爭論了多年的話題能夠暫時落下帷幕了。針對 Watch 的開發不能使用代碼的方式。首先,全部的 WKInterfaceObject 對象都必需要設計的時候經由 StoryBoard 進行添加,運行時咱們沒法再向界面上添加或者移除元素 (若是有移除須要的,可使用隱藏);其次WKInterfaceObject 與佈局相關的某些屬性,好比行高行數等,不可以在運行時進行變動和設定。基原本說在運行時咱們只可以改變視圖的內容,以及經過隱藏某些視圖元素來達到有限地改變佈局 (其餘視圖元素會試圖填充被隱藏的元素)。

總之,代碼進行 UI 編寫的傳統,在 Apple 的不斷努力下,於 WatchKit 發佈的今天,被正式宣判了死刑。

Table 和 Context Menu

大部分 WKInterfaceObject 子類都很直接簡單,可是有兩個我想要單獨說一說,那就是WKInterfaceTable 和 WKInterfaceMenuUITableView 你們都很熟悉了,在 WatchKit 中的 WKInterfaceTable 雖然也是用來展現一組數據,可是由於 WatchKit API 的數據傳輸的特色,使用上相較 UITableView 有很大不一樣和簡化。首先不存在 DataSource 和 Delegate,WKInterfaceTable 中須要呈現的數據數量直接由其實例方法 -setNumberOfRows:withRowType: 進行設定。在進行設定後,使用 -rowControllerAtIndex:枚舉全部的 rowController 進行設定。這裏的 rowController 是在 StoryBoard 中所設定的至關於 UITableViewCell 的東西,只不過和其餘 WKInterfaceObject 同樣,它是直接繼承自NSObject 的。你能夠經過自定義 rowController 並鏈接 StoryBoard 的元素,並在取得rowController 對其進行設定,便可完成 table 的顯示。代碼大概是這樣的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//  MyRowController.swift
import Foundation 
import WatchKit
 
class MyRowController: NSObject { 
     @IBOutlet weak var label: WKInterfaceLabel!
}
 
//  InterfaceController.swift
import WatchKit 
import Foundation
 
class InterfaceController: WKInterfaceController {
 
     @IBOutlet weak var table: WKInterfaceTable!
     let data = ["Index 0","Index 1","Index 2"]
 
     override init(context: AnyObject?) {
         // Initialize variables here.
         super.init(context: context)
 
         // Configure interface objects here.
         NSLog("%@ init", self)
 
         // 注意須要在 StoryBoard 中設置 myRowControllerType
         // 相似 cell 的 reuse id
         table.setNumberOfRows(data.count, withRowType: "myRowControllerType")
         for (i, value) in enumerate(data) {
             if let rowController = table.rowControllerAtIndex(i) as? MyRowController {
                 rowController.label.setText(value)
             }
         }
     }
}

對於點擊事件,並無一個實際的 delegate 存在,而是相似於其餘 WKInterfaceObject 那樣經過 action 將點擊了哪一個 row 做爲參數發送回 WKInterfaceController 進行處理。

另外一個比較好玩的是 Context Menu,這是 WatchKit 獨有的交互,在 iOS 中並不存在。在任意一個 WKInterfaceController 界面中,長按手錶屏幕,若是當前 WKInterfaceController中存在上下文菜單的話,就會嘗試呼出找這個界面對應的 Context Menu。這個菜單最多能夠提供四個按鈕,用來針對當前環境向用戶徵詢操做。由於手錶屏幕有限,在信息顯示的同時再放一些交互按鈕是挺不現實的一件事情,也會很醜。而上下文菜單很好地解決了這個問題,相信長按呼出交互菜單這個操做會成爲從此 Watch App 的一個很標準的交互操做。

添加 Context Menu 很是簡單,在 StoryBoard 裏向 WKInterfaceController 中添加一個 Menu,並在這個 Menu 裏添加對應的 MenuItem 就好了。在 WKInterfaceController 咱們也有對應的 API 來在運行時根據上下文環境進行 MenuItem 的添加 (這是少數幾個容許咱們在運行時添加元素的方法之一)。

1
2
3
4
-addMenuItemWithItemIcon:title:action:
-addMenuItemWithImageNamed:title:action:
-addMenuItemWithImage:title:action:
-clearAllMenuItems

可是 Menu 和 MenuItem 對應的類 WKInterfaceMenu 和 WKInterfaceMenuItem 咱們是沒有辦法拿到的。沒錯,它們甚至都沒有存在於文檔裏 :(

基礎導航

WKInterfaceController 的內建的導航關係基本上分爲三類。首先是像UINavigationController 控制的相似棧的導航方式。相關的 API 有 -pushControllerWithName:context:-popController 以及 -popToRootController。後兩個我想沒必要太多解釋,對於第一個方法,咱們須要使用目標 controller 的 Identifier 字符串 (沒有你只能在 StoryBoard 裏進行設置) 進行建立。context 參數也會被傳遞到目標 controller 的 -initWithContext: 中,因此你能夠以此來在 controller 中進行數據傳遞。

另外一種是咱們你們熟悉的 modal 形式,對應 API 是 -presentControllerWithName:context:和 -dismissController。對於這種導航,和 UIKit 中的不一樣之處就是在目標 controller 中會默認在左上角加上一個 Cancel 按鈕,點擊的話會直接關閉被 present 的 controller。我只想說 Apple 終於想通了,每一個 modal 出來的 controller 都是須要關閉的這個事實…

最後一種導航方式是相似 UIPageController 的分頁式導航。在 iOS app 中,在應用第一次開始時的教學模塊中這種導航方式很是常見,而在 WatchKit 裏能夠說獲得了發揚光大。事實上我我的也認爲這會是 WatchKit 裏最符合使用習慣的導航方式。在 WatchKit 上的 page 導航可能會和 iOS app 的 Tab 導航所提供的功能相對應。

在實現上,page 導航須要在 StoryBoard 中用 segue 的方式將不一樣 page 進行鏈接,新添加的 next page segue 就是幹這個的:

另外 modal 導航的另外一個 API -presentControllerWithNames:contexts: 接受複數個的names 和 context,經過這種方式 modal 呼出的複數個 Controller 也將以 page 導航方式呈現。

固然,做爲 StoryBoard 的經典使用方式,modal 和 push 的導航方式也是能夠在 StoryBoard 中經過 segue 來實現的。同時 WatchKit 也爲 segue 的方式提供了必要的 API。

一些界面實踐

由於整個架構和 UIKit 徹底不一樣,因此不少以前的實踐是沒法直接搬到 WatchKit App 中的。

圖像處理

在 UIKit 中咱們顯示圖片通常使用 UIImageView,而後爲其 image 屬性設置一個建立好的UIImage 對象。而對於 WatchKit 來講,最佳實踐是將圖片存放在 Watch App 的 target 中 (也就是 StoryBoard 的那個 target),在對 WKInterfaceImage 進行圖像設置時,儘可能使用它的 -setImageNamed: 方法。這個方法將只會把圖像名字經過手機傳遞到手錶,而後由手錶在本身的 bundle 中尋找圖片並加載,是最快的途徑。注意咱們的代碼是運行在於手錶的 Watch App 不一樣的設備上的,雖然咱們也能夠先經過 UIImage 的相關方法生成 UIImage 對象,而後再用 -setImage: 或者 -setImageData: 來設置手錶上的圖片,可是這樣的話咱們就須要將圖片放到 Extension 的 target 中,而且須要將圖片的數據經過藍牙傳到手錶,通常來講這會形成不可忽視的延遲,會很影響體驗。

若是對於某些狀況下,咱們只能在 Extension 的 target 中得到圖片 (好比從網絡下載或者代碼動態生成等),而且須要重複使用的話,最好用 WKInterfaceDevice 的 -addCachedImage:name: 方法將其緩存到手錶中。這樣,當咱們以後再使用這張圖片的時候就能夠直接經過 -setImageNamed: 來快速地從手錶上生成並使用了。每一個 app 的 cache 的尺寸大約是 20M,超過的話 WatchKit 將會從最老的數據開始刪除,以騰出空間存儲新的數據。

動畫

由於沒法拿到實際的視圖元素,只有 WKInterfaceObject 這樣的代理對象,以及佈局系統的限制,因此複雜的動畫,尤爲是 UIView 系列或者是 CALayer 系列的動畫是沒法實現的。如今看來惟一可行的是幀動畫,經過爲 WKInterfaceImage 設置包含多個 image 的圖像,或者是經過計時器定時替換圖像的話,能夠實現幀動畫。雖然 Apple 本身的例子也經過這種方法實現了動畫,可是對於設備的存儲空間和能耗均可能會是挑戰,還須要實際拿到設備之後進行實驗和觀察。

其餘 Cocoa Touch 框架的使用

Apple 建議最好不要使用那些須要 prompt 用戶許可的特性,好比 CoreLocation 定位等。由於實際的代碼是在手機上運行的,這類許可也會在手機上彈出,可是用戶並不必定正好在看手機,因此極可能形成體驗降低。另外大部分後臺運行權限也是不建議的。

對於要獲取這些數據和權限,Apple 建議仍是在 iOS app 中完成,並經過 App Groups 進行數據共享,從而在 Watch Extension 中拿到這些數據。

代碼分享

由於如今一個項目會有不少不一樣的 target,因此使用 framework 的方式封裝不一樣 target 的公用部分的代碼,而只在各 target 中實現界面相關的代碼應該是必行的了。這麼作的優勢不只是能夠減小代碼重複,也會使代碼測試和品質獲得提高。若是尚未進行邏輯部分的框架化和測試分離的話,在實現像各種 Extension 或者 Watch App 時可能會遇到很是多的麻煩。

由於若是原有 app 有計劃進行擴展推出各類 Extension 的話,將邏輯代碼抽離並封裝爲 framework 應該是優先級最高的工做。另外新開的項目若是沒有特殊緣由,也強烈建議使用 framework 來組織通用代碼。

Glance 和 Notification

除了 Watch App 本體之外,Glance 和手錶的 Notification 也是重要的使用情景。Notification 雖然概念上比較簡單,可是相對於 iOS 的通知來講是天差地別。WatchKit 的通知容許開發者自行構建界面,咱們能夠經過 payload 設置比較複雜和帶有更多信息的通知,包括圖像,大段文字甚至能夠交互的按鈕,而不是像 iOS 上那樣被限制在文字和一個對話框內。首先不管是經過 Local 仍是 Remote 進行的通知發送會先到達 iPhone,而後再由 iPhone 根據內容判斷是否轉發到手錶。WatchKit App 接收到通知後先會顯示一個簡短的通知,告訴用戶這個 app 有一個通知。若是用戶對通知的內容感興趣的話,能夠點擊或者擡手觀看,這樣由開發者自定義的長版本的通知就會顯現。

Glance 是 WatchKit 的新概念,它容許 Watch App 展現一個佈局固定的WKInterfaceController 頁面。它和 Watch App 本體相對地位至關於 iOS 上的 Today Widget 和 iOS app 自己的地位,是做爲手錶上的 app 的最重要的信息展現出現的。Glance 正如其名,是短時存在的提醒,不能存在可交互的元素。不過若是用戶點擊 Glance 頁面的話,是能夠啓動到 Watch App 的。如今關於 Glance 自己如何啓動和呈現還不是很明確,猜想是某種相似 Today Widget 的呈現方式?(好比按下兩次表側面的旋鈕)

疑問和改進方向

WatchKit 整體使人滿意,提供的 API 和開發環境已經足夠開發者做出一些有趣的東西。可是有幾個如今看來很明顯的限制和但願能增強的方向。

首先是從如今來看 WatchKit 並無提供任何獲取設備傳感信息的 API。不管是心跳、計步或者是用戶是否正在佩戴 Watch 的信息咱們都是拿不到的,這限制了不少數據收集和監視的健康類 app 的製做。若是但願請求數據,仍是不得不轉向使用 HealthKit。可是隨着 iPhone 6 和 6s 的大屏化,在運動時攜帶 iPhone 的人能夠說是變少了。若是 Watch 不能在沒有 iPhone 配對的狀況下收集記錄,並在以後和 iPhone 鏈接後將數據回傳的話,那 Apple 的健康牌就失敗了一大半。相信 Apple 不會放過這種把用戶捆綁的機會…不過若是第三方應用能實時獲取用戶的佩戴情況的話,相信會有不少有意思的應用出現。

另外做爲在發佈會上鼓吹的交互革命的旋鈕和觸感屏幕,如今看來並無開聽任何 API 供開發者使用,因此咱們沒法得知用戶旋轉了手錶旋鈕這個重要的交互事件。如今看來咱們能獲取的操做僅只是用戶點擊屏幕上的按鈕或者拖動滑條這個層級,從這個角度來講,如今的 WatchKit 還遠沒達到能夠顛覆移動應用的地步。

但願以後 Apple 會給咱們帶來其餘的好消息吧。

總之,舞臺已經搭好,以後唱什麼戲,就要看咱們的了。

相關文章
相關標籤/搜索