你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Apple WatchKit 初探

Apple WatchKit 初探

編輯:IOS開發基礎

apple-watch1.gif

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

當然,因為設備本身的無論是電量還是運算能力的限制,在進行 Watch app 開發的時候也還是有很多掣肘。現在 Watch app 僅僅只是作為視圖顯示和回傳用戶交互的存在,但是考慮到這是這款設備的第一版 SDK,另外 Apple 也有承諾之後會允許真正運行在 Watch 上的 app 的出現,Apple Watch 和 WatchKit 的未來還是很值得期待的。

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

我們能做什麼

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 和體驗也許是被允許的。

QQ截圖20141120092604.jpg

在應用安裝時,負責邏輯部分的 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 的話,可以將它們理解為分別對應 -viewDidLoad,viewWillAppear: 以及 -viewDidDisappear:。雖然看方法名和實際使用上可能你會認為 -initWithContext: 應該對應 UIViewController 的 init 或者initWithCoder: 這樣的方法,但是事實上在 -initWithContext: 時 WKInterfaceController 中的“視圖元素” (請注意這裡我加上了引號,因為它們不是真正的視圖,稍後會再說明) 都已經初始化完畢可用,這其實和 -viewDidLoad 中的行為更加相似。

我們一般在 -initWithContext: 和 -willActivate 中配置“視圖元素”的屬性,在 -didDeactivate 中停用像是 NSTimer 之類的會 hold 住 self 的對象。需要特別注意的是,在 -didDeactivate 中對“視圖元素”屬性進行設置是無效的,因為當前的WKInterfaceController 已經非活躍。

WKInterfaceObject 及其子類

WKInterfaceObject 負責具體的界面元素設置,包括像是 WKInterfaceButton,WKInterfaceLabel 或 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 來在行內進行“列”布局。這帶來了相對簡單的布局實現,當然,同時也是對界面交互的設計的一種挑戰。

QQ截圖20141120092505.jpg

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

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

Table 和 Context Menu

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

//??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)
????????????}
????????}
????}
}

012.jpg

對於點擊事件,並沒有一個實際的 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 的添加 (這是少數幾個允許我們在運行時添加元素的方法之一)。

-addMenuItemWithItemIcon:title:action:
-addMenuItemWithImageNamed:title:action:
-addMenuItemWithImage:title:action:
-clearAllMenuItems

QQ截圖20141120092222.jpg

但是 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 就是干這個的:

watchkit-5.png

另外 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 會給我們帶來其他的好消息吧。

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

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved