天天看點

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,如下圖:

WatchKit Framework

其中 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;
           

當Watch OS加載App中的Storyboard時,iPhone端也會開始加載對應的WatchKit Extension。

WatchKit Framework

當Watch OS開始初始化我們Watch App的Storyboard中的UI時,iPhone端WatchKit Extension會生成對應的

WKInterfaceController

,并且響應

initWithContext:

方法。

當Watch OS顯示目前加載的UI時,WatchKit Extension中對應的

WKInterfaceController

響應

willActivate

方法。

當使用者切換頁面或者停止使用時,WatchKit Extension中對應的

WKInterfaceController

響應

didDeactivate

方法。

WatchKit Framework

從上圖可知這三個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;
           

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;
- (NSArray *)contextsForSegueWithIdentifier:(NSString *)segueIdentifier;
- (id)contextForSegueWithIdentifier:(NSString *)segueIdentifier inTable:(WKInterfaceTable *)table rowIndex:(NSInteger)rowIndex;
- (NSArray *)contextsForSegueWithIdentifier:(NSString *)segueIdentifier inTable:(WKInterfaceTable *)table rowIndex:(NSInteger)rowIndex;
           

當我們在應用設計的階段就知道需要跳轉的下一個

WKInterfaceController

時,我們可以直接在Storyboard中設定Triggered Segues。使用Segues時,Selection同樣支援Push和Model兩種跳轉方式。

我們可以使用上面一組API進行跳轉中的資料傳遞。

響應互動事件

WKInterfaceObject中像Button,Slider, Switch等控件可以和使用者互動,我們和往常一樣,可以在

WKInterfaceController

實作對應的Action,标記為IBAction,然後連接配接到Storyboard中。

這裡特别的地方是,當我們的

WKInterfaceController

中包含

WKInterfaceTable

執行個體時,我們可以通過實作預設的

- (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex

方法響應table中每一行的點選事件,這裡和往常的UITableView的實作方式不太一樣,更加簡單。

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,它看起來像這樣:

WatchKit Framework

我們不但可以通過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;
           

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;
           

我們可以通過

- (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"]];
    }];
}
           

我們同樣可以使用下面的API進行添加,删除Table的Rows:

- (void)insertRowsAtIndexes:(NSIndexSet *)rows withRowType:(NSString *)rowType;
- (void)removeRowsAtIndexes:(NSIndexSet *)rows;
           

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;
           

另外我們可以使用這個類中的以下一組方法來緩存圖檔,以備将來繼續使用:

- (void)addCachedImage:(UIImage *)image name:(NSString *)name;
- (void)addCachedImageWithData:(NSData *)imageData name:(NSString *)name;
- (void)removeCachedImageWithName:(NSString *)name;
- (void)removeAllCachedImages;
           

已經緩存的圖檔,可以使用

WKInterfaceImage

中下面的API直接讀取:

- (void)setImageData:(NSData *)imageData;
- (void)setImageNamed:(NSString *)imageName;
           

WatchKit允許每一個App最多緩存20MB的圖檔,如果超過的話,WatchKit将從最老的資料開始删除,為新資料騰出空間。

總結

關于WatchKit Framework中API的知識點都基本包含在了上述筆記中。目前所提供的API功能有限,主要是資訊的顯示,通知的接收。更多關于多媒體或者傳感器方面的API在這個版本中并沒有開放,期待蘋果的下一次更新。

繼續閱讀