Watch開發:Complications教程

Complications 是 watchOS 2 新加入的特性,它是表盤上的小界面元素,用于自定義表盤,可以支持直接從表盤喚起自己的App。

Paste_Image.png

蘋果提供了一個新的FrameWork ClockKit, ClockKit 會提供一些模板CLKComplicationTemplate給我們,我們選擇樣式、填充自己想要展示的數據,ClockKit會在某時間點向我們請求數據, 并負責將其渲染在表盤上。

新建項目

  1. watchOS > Application > iOS App with WatchKit App
Paste_Image.png

2.配置,勾選Include Complication

Paste_Image.png

3.如果項目已經創建,打開Target -> WatchKit Extension.你會看到Complications Configuration配置項。Supported Families 底下的五項代表著app要支持的complication families

Paste_Image.png

這是這些Families成員對應的在表盤上的風格


Paste_Image.png

4.創建complication
打開ClockKit Introduction WatchKit Extension中的ComplicationController,這個文件是勾選了Include Complication后Xcode自動為我們生成的。它包含一個ComplicationController類,實現了CLKComplicationDataSource協議. CLKComplicationDataSource中的方法決定了你向ClockKit提供數據的方式,這些方法里都有個handler的參數,你需要在自己的實現中回調 handle,并把自己的數據作為handler回傳給ClockKit. 這是我們和ClockKit通信的方式:

Paste_Image.png

數據模版 Template:

Template是ClockKit為開發者定義的數據模型,但是它并不單純是數據模型,不同的模版有不同視圖布局的風格。

比如,對5種Complication類型,類型和布局:

typedef NS_ENUM(NSInteger, CLKComplicationFamily) {
    CLKComplicationFamilyModularSmall                                                         = 0,
    CLKComplicationFamilyModularLarge                                                         = 1,
    CLKComplicationFamilyUtilitarianSmall                                                     = 2,
    CLKComplicationFamilyUtilitarianSmallFlat   /* subset of UtilitarianSmall */              = 6,
    CLKComplicationFamilyUtilitarianLarge                                                     = 3,
    CLKComplicationFamilyCircularSmall                                                        = 4,
    CLKComplicationFamilyExtraLarge                                                           = 7,
};
Paste_Image.png
Paste_Image.png

比如,對于CLKComplicationTemplateModularLargeStandardBody
這個類,它的屬性和布局對應如下:

Paste_Image.png

ImageProviders|CLKTextProvider

在 WWDC中說,這里用ImageProvider而不是UIImage, 用TextProvider而不是NSString,是因為在watch表盤的complication視圖尺寸很小,防止出現文本截斷等一些不好的體驗,開發者使用Provider可以更好說明自己的意圖 , 同時ClockKit會為我們做布局和顯示的其他工作。

Paste_Image.png
@interface Show : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *shortName;
@property (nonatomic, strong) NSString *genre;
@property (nonatomic, strong) NSDate *startDate;
@property (nonatomic, assign) NSTimeInterval length;
@end

@implementation Show

- (instancetype)initWithName:(NSString *)name
                   shortName:(NSString *)shortName
                       genre:(NSString *)genre
                   startDate:(NSDate *)startDate
                      length:(NSTimeInterval)length
{
    self = [super init];
    _name = name;
    _shortName = shortName;
    _genre = genre;
    _startDate = startDate;
    _length = length;
    return self;
}

//  App第一次啟動時,ClockKit會調用這個方法來獲取一個complications 模版,作為placeHolder模版展示。
- (void)getPlaceholderTemplateForComplication:(CLKComplication *)complication withHandler:(void (^)(CLKComplicationTemplate * _Nullable))handler
{
    Show *s = [[Show alloc] initWithName:@"Intdo the Wild" shortName: @"Into Wild" genre:@"Documentary" startDate:[NSDate date] length:hour*1.5];
    Show *s2 = [[Show alloc] initWithName:@"24/7" shortName: nil genre:@"Drama" startDate:[NSDate dateWithTimeIntervalSinceNow:hour*1.5] length:hour];
    Show *s3 = [[Show alloc] initWithName:@"How to become rich" shortName: @"Become Rich" genre:@"Documentary" startDate:[NSDate dateWithTimeIntervalSinceNow:hour*2.5] length:hour];
    _shows = @[s, s2, s3];
    
    CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init];
    template.headerTextProvider = [CLKTimeIntervalTextProvider textProviderWithStartDate:[NSDate date] endDate:[NSDate dateWithTimeIntervalSinceNow:60*60*1.5]];
    template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:@"Short Name" shortText:@"Name"];
    template.body2TextProvider = [CLKSimpleTextProvider textProviderWithText:@"Show Genre" shortText:nil];
    
    handler(template);
}

TimeLines

TimeLine是ClockKit抽象出來的一個時間軸。開發者的數據單元用CLKComplicationTimelineEntry 模型包裝,綁定在timeline上某一點時刻. 當時間走到那一刻,ClockKit會自動將我們傳給的數據內容展示在表盤。在某個時間段中,ClockKit 會一直展示上一刻展示的數據, 直到下一刻數據更新點。如下圖:

Paste_Image.png

在[11, 12) 這個域,表盤上的溫度展示的是11am的時候綁定的數據55。12PM這一刻,又更新為56.
再比如說日歷應用需要提前提醒用戶"4:00-5:00要剪頭發",開發者就需要把"4:00-5:00要剪頭發"這個提示綁定在1PM這個時刻上,而不是直接綁在4PM這個時間點上。 如下圖:

Paste_Image.png

CLKComplicationDataSource協議類中跟timeline相關的方法

// 時間前進的方向,如果是回憶的時間軸,可以選CLKComplicationTimeTravelDirectionBackward
- (void)getSupportedTimeTravelDirectionsForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimeTravelDirections directions))handler {
   handler(CLKComplicationTimeTravelDirectionForward);
}
?
// 時間軸的起始點,ClockKit回調這個方法獲取最早一個時刻,我們在實現中調用hander這個Block來給ClockKit傳遞
// 那一刻需要展示的數據,因為不需要展示過去的數據,這里我們用當前時間
- (void)getTimelineStartDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler {
   handler([NSDate date]);
}

?
- (void)getTimelineEndDateForComplication:(CLKComplication *)complication withHandler:(void(^)(NSDate * __nullable date))handler {
   handler([NSDate dateWithTimeIntervalSinceNow:60*60*24]);
}

CLKComplicationTimelineEntry

在代碼實現中,CLKComplicationTimelineEntry這個類,包含一個date屬性,可以將Template數據綁定在時間軸的某一個時刻上。

Paste_Image.png
Paste_Image.png

獲取當前時刻的單條數據

- (void)getCurrentTimelineEntryForComplication:(CLKComplication *)complication withHandler:(void(^)(CLKComplicationTimelineEntry * __nullable))handler
{
    // Call the handler with the current timeline entry
    Show *s = _shows[0];
    CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init];
    template.headerTextProvider = [CLKTimeIntervalTextProvider textProviderWithStartDate:s.startDate
                                                                                 endDate:[NSDate dateWithTimeInterval:s.length sinceDate:s.startDate]
                                                                                timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT+0900"]];
    template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:s.name shortText:s.shortName];
    template.body2TextProvider = [CLKSimpleTextProvider textProviderWithText:s.genre shortText:nil];
    // 數據綁定的時間為當前時間
    CLKComplicationTimelineEntry *entry = [CLKComplicationTimelineEntry entryWithDate:[NSDate date] complicationTemplate:template];
    
    handler(entry);
}

ClockKit會回調這個方法,Extension通過調用handler,向ClockKit回傳數據


Paste_Image.png

綁定多條數據

那么如果我想給ClockKit提供其他時刻的數據呢? 這里,我們在時間軸上每15分鐘綁定一條數據,我們需要用到- (void)getTimelineEntriesForComplication:(CLKComplication *)complication afterDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler方法。

- (void)getTimelineEntriesForComplication:(CLKComplication *)complication afterDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler
{
    // Call the handler with the timeline entries after to the given date
    NSMutableArray *entries = [[NSMutableArray alloc] init];
    for (Show *s in _shows)
    {
        // ClockKit有個數限制
        if (entries.count < limit && [s.startDate timeIntervalSinceDate:date] >0)
        {
            CLKComplicationTemplateModularLargeStandardBody *template = [[CLKComplicationTemplateModularLargeStandardBody alloc] init];
            template.headerTextProvider = [CLKTimeIntervalTextProvider textProviderWithStartDate:s.startDate
                                                                                         endDate:[NSDate dateWithTimeInterval:s.length sinceDate:s.startDate]
                                                                                        timeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT+0900"]];
            template.body1TextProvider = [CLKSimpleTextProvider textProviderWithText:s.name shortText:s.shortName];
            template.body2TextProvider = [CLKSimpleTextProvider textProviderWithText:s.genre shortText:nil];
            // 數據綁定在時間軸不同的時刻上
            CLKComplicationTimelineEntry *entry = [CLKComplicationTimelineEntry entryWithDate:[NSDate dateWithTimeInterval:s.length sinceDate:s.startDate] complicationTemplate:template];
            [entries addObject:entry];
        }
    }
    
    // 數組回傳給ClockKit
    handler(entries);
}

如圖


Paste_Image.png

如果要綁定在當前時間之前的數據實現另一個方法

Paste_Image.png

現在進行真機調試,Xcode schema中選擇Complication,手機和watch提前配置好,run

調試

  1. 按下 Digital Crown 以前往表盤。
  1. 用力按壓顯示屏。模擬器 Command+Shift+2


    Paste_Image.png
  2. 向左或向右輕掃以選取某個表盤,然后輕點“自定”。 模擬器 Command+Shift+1
Paste_Image.png
  1. 向左或向右輕掃以選擇某個功能,然后轉動 Digital Crown 對其進行更改。例如,您可以更改秒針的顏色或表盤上的標記。
Paste_Image.png
  1. 向左輕掃至最左邊,以編輯功能欄。輕點某個功能欄以將其選中,然后轉動 Digital Crown 對其進行更改。您也可以添加來自其他應用的功能欄。

  2. 完成后,按下 Digital Crown 以存儲您的更改。

文字沒顯示出來,呃,先到這兒,明天看看。

Demo


參考文章
https://developer.apple.com/library/content/documentation/General/Conceptual/WatchKitProgrammingGuide/ComplicationEssentials.html#//apple_ref/doc/uid/TP40014969-CH27-SW1
https://code.tutsplus.com/tutorials/an-introduction-to-clockkit--cms-24247
http://www.informit.com/articles/article.aspx?p=2429818
Templates 和Provider https://www.bignerdranch.com/blog/watchkit-2-complications/
http://www.sneakycrab.com/blog/2015/6/10/writing-your-own-watchkit-complications

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容