Complications 是 watchOS 2 新加入的特性,它是表盘上的小界面元素,用于自定义表盘,可以支持直接从表盘唤起自己的App。
![1240](http://upload-images.jianshu.io/upload_images/1126909-4d6f4ae51ff22099.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
苹果提供了一个新的FrameWork ClockKit
, ClockKit 会提供一些模板CLKComplicationTemplate给我们,我们选择样式、填充自己想要展示的数据,ClockKit会在某时间点向我们请求数据, 并负责将其渲染在表盘上。
新建项目
- watchOS > Application > iOS App with WatchKit App
![1240](http://upload-images.jianshu.io/upload_images/1126909-704956e462c4cfe6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2.配置,勾选Include Complication
![1240](http://upload-images.jianshu.io/upload_images/1126909-df26244938b22f37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
3.如果项目已经创建,打开Target -> WatchKit Extension.你会看到Complications Configuration配置项。Supported Families 底下的五项代表着app要支持的complication families
![1240](http://upload-images.jianshu.io/upload_images/1126909-e52f205b97b41172.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这是这些Families成员对应的在表盘上的风格
![1240](http://upload-images.jianshu.io/upload_images/1126909-2f8e02721054a629.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
4.创建complication
打开ClockKit Introduction WatchKit Extension
中的ComplicationController
,这个文件是勾选了Include Complication后Xcode自动为我们生成的。它包含一个ComplicationController
类,实现了CLKComplicationDataSource
协议. CLKComplicationDataSource
中的方法决定了你向ClockKit提供数据的方式,这些方法里都有个handler的参数,你需要在自己的实现中回调 handle,并把自己的数据作为handler回传给ClockKit. 这是我们和ClockKit通信的方式:
![1240](http://upload-images.jianshu.io/upload_images/1126909-fc1482e191aa9591.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
数据模版 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,
};
![1240](http://upload-images.jianshu.io/upload_images/1126909-6ab96b4745efe047.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![1240](http://upload-images.jianshu.io/upload_images/1126909-94d7a4bed991b516.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
比如,对于CLKComplicationTemplateModularLargeStandardBody
这个类,它的属性和布局对应如下:
![1240](http://upload-images.jianshu.io/upload_images/1126909-3fec919425439c80.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
ImageProviders|CLKTextProvider
在 WWDC中说,这里用ImageProvider而不是UIImage, 用TextProvider而不是NSString,是因为在watch表盘的complication视图尺寸很小,防止出现文本截断等一些不好的体验,开发者使用Provider可以更好说明自己的意图 , 同时ClockKit会为我们做布局和显示的其他工作。
![1240](http://upload-images.jianshu.io/upload_images/1126909-579ed1833b44d684.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
@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 会一直展示上一刻展示的数据, 直到下一刻数据更新点。如下图:
![1240](http://upload-images.jianshu.io/upload_images/1126909-a636fa8c0bb032eb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
在[11, 12) 这个域,表盘上的温度展示的是11am的时候绑定的数据55。12PM这一刻,又更新为56.
再比如说日历应用需要提前提醒用户"4:00-5:00要剪头发",开发者就需要把"4:00-5:00要剪头发"这个提示绑定在1PM这个时刻上,而不是直接绑在4PM这个时间点上。 如下图:
![1240](http://upload-images.jianshu.io/upload_images/1126909-df8c1d2a6f65fd39.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
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数据绑定在时间轴的某一个时刻上。
![1240](http://upload-images.jianshu.io/upload_images/1126909-3a4d2716f71781a6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![1240](http://upload-images.jianshu.io/upload_images/1126909-2634f9a4fef160c0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
获取当前时刻的单条数据
- (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回传数据
![1240](http://upload-images.jianshu.io/upload_images/1126909-aae5a76ffb0a005f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
绑定多条数据
那么如果我想给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);
}
如图
![1240](http://upload-images.jianshu.io/upload_images/1126909-9b37737d08dbb4ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如果要绑定在当前时间之前的数据实现另一个方法
![1240](http://upload-images.jianshu.io/upload_images/1126909-9b37737d08dbb4ba.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
现在进行真机调试,Xcode schema中选择Complication,手机和watch提前配置好,run
调试
- 按下 Digital Crown 以前往表盘。
- 用力按压显示屏。模拟器 Command+Shift+2
Paste_Image.png - 向左或向右轻扫以选取某个表盘,然后轻点“自定”。 模拟器 Command+Shift+1
![1240](http://upload-images.jianshu.io/upload_images/1126909-008f3cd15beb6ee9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 向左或向右轻扫以选择某个功能,然后转动 Digital Crown 对其进行更改。例如,您可以更改秒针的颜色或表盘上的标记。
![1240](http://upload-images.jianshu.io/upload_images/1126909-5c00b251bdfee54e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
向左轻扫至最左边,以编辑功能栏。轻点某个功能栏以将其选中,然后转动 Digital Crown 对其进行更改。您也可以添加来自其他应用的功能栏。
-
完成后,按下 Digital Crown 以存储您的更改。
文字没显示出来,呃,先到这儿,明天看看。
参考文章
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