天天看點

********走進 WatchKit Framework***********

WatchKit Apple提供的開發專題頁面如下: https://developer.apple.com/watchkit/。其中包含兩個Demo,這兩個Demo可以讓大家快速的了解WatchKit的建構。

你可以先看看:Apple WatchKit 初探,作者為onevcat,對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。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

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對應了幾個不同的狀态:

1 2 3

- (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進行頁面跳轉:

1 2 3 4 5 6 7 8 9

- (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時有四點需要注意:

1. Push和Present方法第一個參數是對應的在Storyboard中為WKInterfaceController設定的identifier字元串。WatchKit Extension使用這幾個API向Watch OS傳遞消息,真實的UI加載渲染行為是在Watch端進行。

2. popToRootController是跳轉到Watch App的Storyboard中Main Entry Point對應的Controller。

3. presentControllerWithNames, 我們可以present一組Controller, 這一組Controller将以page control的形式展示。

4. becomeCurrentPage 當頁面是以page control的形式展現時,我們可以調用這個方法改變目前的page

另外一組API是:

1 2 3 4

- (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獲得通知内容,并設定處理完成的回調Block。

1 2

- (void)didReceiveRemoteNotification:(NSDictionary *)remoteNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler

- (void)didReceiveLocalNotification:(UILocalNotification *)localNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler

Menu

我們可以通過Storyboard在界面中添加Menu,它看起來像這樣:

********走進 WatchKit Framework***********

我們不但可以通過Storyboard在Menu中添加Item,也可以通過WKInterfaceController中以下一組API,在上下文環境中添加相應的Item:

1 2 3 4

- (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。

1 2

- (void)setRowTypes:(NSArray *)rowTypes;

- (void)setNumberOfRows:(NSInteger)numberOfRows withRowType:(NSString *)rowType;

我們可以通過- (id)rowControllerAtIndex:(NSInteger)index獲得某一行對應的Row Controller。下面是一段在interface controller中初始化Table Rows的例子:

1 2 3 4 5 6 7 8 9

- (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:

1 2

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

- (void)removeRowsAtIndexes:(NSIndexSet *)rows;

WKInterfaceDevice

這是一個單例類,可以獲得目前Apple Watch的部分資訊。目前公開的資訊有:

1 2 3 4

@property(nonatomic,readonly) CGRect    screenBounds;

@property(nonatomic,readonly) CGFloat   screenScale;

@property(nonatomic,readonly,strong) NSLocale *currentLocale;

@property(nonatomic,readonly,copy)  NSString *preferredContentSizeCategory;

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

1 2 3 4

- (void)addCachedImage:(UIImage *)image name:(NSString *)name;

- (void)addCachedImageWithData:(NSData *)imageData name:(NSString *)name;

- (void)removeCachedImageWithName:(NSString *)name;

- (void)removeAllCachedImages;

已經緩存的圖檔,可以使用WKInterfaceImage中下面的API直接讀取:

1 2

- (void)setImageData:(NSData *)imageData;

- (void)setImageNamed:(NSString *)imageName;

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

總結

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