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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容