走進 WatchKit Framework
寫在前面
WatchKit Apple提供的開發專題頁面如下: https://developer.apple.com/watchkit/。其中包含兩個Demo,這兩個Demo可以讓大家快速的了解WatchKit的構建。
Watch App Architecture
每一個Apple Watch App和 iOS Extension一樣仍然需要依賴一個主體App,Apple Watch App 包含兩個部分:Watch App 和 WatchKit Extension,如下圖:
其中 Watch App 部分位于用戶的Apple Watch上,它目前為止只允許包含Storyboard文件和Resources文件。在我們的項目里,這一部分不包括任何代碼。
WatchKit Extension 部分位于用戶的iPhone安裝的對應App上,這里包括我們需要實現的代碼邏輯和其他資源文件。
這兩個部分之間就是通過 WatchKit
進行連接通訊。
WatchKit
WatchKit用來為開發者構建Apple Watch App。它所有的類如下,其中最上層的類繼承于NSObject
。
WKInterfaceController WKUserNotificationInterfaceController WKInterfaceDevice WKInterfaceObject WKInterfaceButton WKInterfaceDate WKInterfaceGroup WKInterfaceImage WKInterfaceLabel WKInterfaceMap WKInterfaceSeparator WKInterfaceSlider WKInterfaceSwitch WKInterfaceTable WKInterfaceTimer
WKInterfaceController
WKInterfaceController
是我們開發Watch App的核心類,它的地位和之前使用的UIViewController
一樣。
每一個Watch App構建時,至少需要在Storyboard上設置一個WKInterfaceController
實例作為程序入口。我們可以在Storyboard上使用Main Entry Point
設置。
當用戶launch了Watch App時,Watch OS 會開始加載程序中的Storyboard。我們在Storyboard中為每一個WKInterfaceController
設置的響應事件,會在用戶觸發時在WatchKit Extension中響應。我們可以像以前一樣push, pop, present 目標WKInterfaceController
。
生命周期
WKInterfaceController
一樣也有自己的生命周期,以下幾個API對應了幾個不同的狀態:
- (instancetype)initWithContext:(id)context;
- (void)willActivate;
- (void)didDeactivate;</pre>
當Watch OS加載App中的Storyboard時,iPhone端也會開始加載對應的WatchKit Extension。
當Watch OS開始初始化我們Watch App的Storyboard中的UI時,iPhone端WatchKit Extension會生成對應的
WKInterfaceController
,并且響應initWithContext:
方法。當Watch OS顯示當前加載的UI時,WatchKit Extension中對應的
WKInterfaceController
響應willActivate
方法。當用戶切換頁面或者停止使用時,WatchKit Extension中對應的
WKInterfaceController
響應didDeactivate
方法。從上圖可知這三個API,對應了Watch OS加載一個視圖控制器的三個狀態。我們在自己的
WKInterfaceController
類中,應該實現這三個API用來處理不同的情況:- initWithContext: 我們可以在這里加載數據或者更新在StoryBoard中當前Controller添加的interface objects。
- willActivate 我們可以在這里更新interface objects或者處理其他事件
- didDeactivate 我們應該在這里清理task或者數據。在這里更新interface objects將會被系統忽略。
頁面跳轉
當用戶和我們的APP進行交互時,有很多時候,我們需要進行頁面的跳轉。
WKInterfaceController
目前支持兩組API進行頁面跳轉:- (void)pushControllerWithName:(NSString *)name context:(id)context
- (void)popController;
(void)popToRootController;
(void)presentControllerWithName:(NSString *)name context:(id)context;
- (void)presentControllerWithNames:(NSArray )names contexts:(NSArray )contexts;
(void)dismissController;
(void)becomeCurrentPage;</pre>
Push,Pop, Present, Dismiss的行為和UIViewController中類似。我們可以在代碼中,根據程序上下文的狀態,控制跳轉到某一個頁面。
使用這一組API時有四點需要注意:
- Push和Present方法第一個參數是對應的在Storyboard中為
WKInterfaceController
設置的identifier字符串。WatchKit Extension使用這幾個API向Watch OS傳遞消息,真實的UI加載渲染行為是在Watch端進行。 - popToRootController是跳轉到Watch App的Storyboard中
Main Entry Point
對應的Controller。 - presentControllerWithNames, 我們可以present一組Controller, 這一組Controller將以page control的形式展示。
- becomeCurrentPage 當頁面是以page control的形式展現時,我們可以調用這個方法改變當前的page
另外一組API是:
- (id)contextForSegueWithIdentifier:(NSString *)segueIdentifier;
- Push和Present方法第一個參數是對應的在Storyboard中為
- (NSArray )contextsForSegueWithIdentifier:(NSString )segueIdentifier;
- (id)contextForSegueWithIdentifier:(NSString )segueIdentifier inTable:(WKInterfaceTable )table rowIndex:(NSInteger)rowIndex;
- (NSArray )contextsForSegueWithIdentifier:(NSString )segueIdentifier inTable:(WKInterfaceTable )table rowIndex:(NSInteger)rowIndex;</pre>
當我們在應用設計的階段就知道需要跳轉的下一個
WKInterfaceController
時,我們可以直接在Storyboard中設置Triggered Segues。使用Segues時,Selection同樣支持Push和Model兩種跳轉方式。我們可以使用上面一組API進行跳轉中的數據傳遞。
響應交互事件
WKInterfaceObject中像Button,Slider, Switch等控件可以和用戶交互,我們和往常一樣,可以在
WKInterfaceController
實現對應的Action,標記為IBAction,然后連接到Storyboard中。這里特別的地方是,當我們的
)table didSelectRowAtIndex:(NSInteger)rowIndex</code>方法響應table中每一行的點擊事件,這里和往常的UITableView的實現方式不太一樣,更加簡單。</p>WKInterfaceController
中包含WKInterfaceTable
實例時,我們可以通過實現默認的- (void)table:(WKInterfaceTable
Glance
Glance是 Watch App上新的概念,它主要作用是給用戶一個短時的提醒。我們可以通過Storyboard創建一個Glance interface Controller.對應的WatchKit Extension中,它同樣需要繼承于
WKInterfaceController
,享有同樣的生命周期。我們可以在其中實現自己的邏輯。這里需要注意的是,Glance是可以和用戶進行交互的。當用戶Tap Glance頁面時,會跳轉到我們的Watch App中。這里可以在自定義的GlanceInterfaceController中使用
- (void)updateUserActivity:(NSString *)type userInfo:(NSDictionary *)userInfo
傳遞數據。比如我們需要在用戶點擊Glance之后進入到某一個特定的頁面,我們可以把目標頁面的identifier和要傳遞的其他消息包裝到字典中,然后在Initial Interface Controller中實現- (NSString *)actionForUserActivity:(NSDictionary *)userActivity context:(id *)context
方法跳轉到目標頁面,這里的userActivity就是上文傳遞的userInfo,返回的NSString是目標頁面的identifier,context指針是目標頁面initWithContext
中context數據。Notification && WKUserNotificationInterfaceController
當我們的主體App支持Notification時,Apple Watch將能夠顯示這些通知。Watch OS提供了默認的通知顯示,當用戶點擊通知進入我們的App時,Initial Interface Controller中
- (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)remoteNotification
或者- (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)localNotification
方法將會被響應,我們可以通過實現這兩個方法獲得通知的消息,跳轉到目標頁面。我們同樣可以通過Storyboard創建一個Notification interface Controller,這樣可以實現自定義的通知界面。對應的WatchKit Extension中,它繼承于
WKUserNotificationInterfaceController
,享有和WKInterfaceController
同樣的生命周期。我們可以通過實現下面兩組API- (void)didReceiveRemoteNotification:(NSDictionary *)remoteNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler
或者- (void)didReceiveLocalNotification:(UILocalNotification *)localNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler
獲得通知內容,并設置處理完成的回調Block。Menu
我們可以通過Storyboard在界面中添加Menu,它看起來像這樣:
我們不但可以通過Storyboard在Menu中添加Item,也可以通過
WKInterfaceController
中以下一組API,在上下文環境中添加相應的Item:- (void)addMenuItemWithImage:(UIImage )image title:(NSString )title action:(SEL)action;
- (void)addMenuItemWithImageNamed:(NSString )imageName title:(NSString )title action:(SEL)action;
- (void)addMenuItemWithItemIcon:(WKMenuItemIcon)itemIcon title:(NSString *)title action:(SEL)action;
- (void)clearAllMenuItems;</pre>
WKInterfaceObject
WKInterfaceObject
負責界面的元素,目前Apple公開了11個具體的子類用來展現各種不同類型的元素。它和之前的UIView
或者UIView的子類不一樣,WKInterfaceObject只負責在WatchKit Extension和Watch App中傳遞相應的事件,具體的UI渲染在Watch App中完成。Watch App 采取的布局方式和 iOS App 完全不同。我們無法指定某個視圖的具體坐標,也不能使用AutoLayout來進行布局。WatchKit只能在以“行”為基本單位進行布局。在一行中如果要顯示多個元素,我們就要通過
WKInterfaceGroup
在行內進行列
布局。WKInterfaceTable
和學習iOS開發一樣,先從一個TableView開始上手。目前在WatchKit中最復雜的界面元素也是
WKInterfaceTable
。我們可以通過Storyboard直接在當前
WKInterfaceController
中添加一個Table
,每一個Table默認包含一個Table Row Controller
, 這個Table Row Controller作用相當于之前的Cell
,不過這里是繼承于NSObject
。我們可以使用Table Row Controller中定義每一種Row的樣式,然后設置一個唯一的identifier用來區分。我們可以通過以下兩組設置Table的每一行的樣式,rowType對應Storyboard中Row Controller的identifier。
- (void)setRowTypes:(NSArray *)rowTypes;
(void)setNumberOfRows:(NSInteger)numberOfRows withRowType:(NSString *)rowType;</pre>
我們可以通過
- (id)rowControllerAtIndex:(NSInteger)index
獲得某一行對應的Row Controller。下面是一段在interface controller中初始化Table Rows的例子:- (void)loadTableRows { [self.interfaceTable setNumberOfRows:self.elementsList.count withRowType:@"default"];
// Create all of the table rows. [self.elementsList enumerateObjectsUsingBlock:^(NSDictionary rowData, NSUInteger idx, BOOL stop) {
AAPLElementRowController *elementRow = [self.interfaceTable rowControllerAtIndex:idx]; [elementRow.elementLabel setText:rowData[@"label"]];
}]; }</pre>
我們同樣可以使用下面的API進行添加,刪除Table的Rows:
- (void)insertRowsAtIndexes:(NSIndexSet )rows withRowType:(NSString )rowType;
- (void)removeRowsAtIndexes:(NSIndexSet )rows;</pre>
WKInterfaceDevice
這是一個單例類,可以獲得當前Apple Watch的部分信息。目前公開的信息有:
@property(nonatomic,readonly) CGRect screenBounds; @property(nonatomic,readonly) CGFloat screenScale; @property(nonatomic,readonly,strong) NSLocale
currentLocale; @property(nonatomic,readonly,copy) NSString preferredContentSizeCategory;</pre>另外我們可以使用這個類中的以下一組方法來緩存圖片,以備將來繼續使用:
- (void)addCachedImage:(UIImage
)image name:(NSString *)name; - (void)addCachedImageWithData:(NSData )imageData name:(NSString )name;
- (void)removeCachedImageWithName:(NSString *)name;
- (void)removeAllCachedImages;</pre>
已經緩存的圖片,可以使用
WKInterfaceImage
中下面的API直接讀取:- (void)setImageData:(NSData *)imageData;
- (void)setImageNamed:(NSString *)imageName;</pre>
WatchKit允許每一個App最多緩存20MB的圖片,如果超過的話,WatchKit將從最老的數據開始刪除,為新數據騰出空間。
總結
關于WatchKit Framework中API的知識點都基本包含在了上述筆記中。目前所提供的API功能有限,主要是信息的顯示,通知的接收。更多關于多媒體或者傳感器方面的API在這個版本中并沒有開放,期待蘋果的下一次更新。
- 本文是關于WatchKit Framework的學習筆記。有不對的地方,歡迎大家指正。
- 本文對應參考文獻為 WatchKit Framework Reference from Apple Delevoper
- 本文所有圖片來源于Apple Delevoper
- 本文由@Chun發表于Chun Tips .
- 版權聲明:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0