Watch开发:Complications教程

MilXjy 8年前
   <p>Complications 是 watchOS 2 新加入的特性,它是表盘上的小界面元素,用于自定义表盘,可以支持直接从表盘唤起自己的App。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/28eb5335f4316551ad5f64210cd3e345.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>苹果提供了一个新的FrameWork ClockKit , ClockKit 会提供一些模板CLKComplicationTemplate给我们,我们选择样式、填充自己想要展示的数据,ClockKit会在某时间点向我们请求数据, 并负责将其渲染在表盘上。</p>    <h2><strong>新建项目</strong></h2>    <ol>     <li><strong>watchOS > Application > iOS App with WatchKit App</strong></li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7920b1b839cf124dffe066cfd310def5.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>2.配置,勾选 <strong>Include Complication</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1af98efefcdfb2713790ac2d8dadf10f.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>3.如果项目已经创建,打开Target -> WatchKit Extension.你会看到 <strong>Complications Configuration</strong> 配置项。 <strong>Supported Families</strong> 底下的五项代表着app要支持的complication families</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/936626552b65a566d4eb07fe2cd02ff1.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>这是这些Families成员对应的在表盘上的风格</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8cd01c6f011c8c3bac97013947b8125e.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>4.创建complication</p>    <p>打开 ClockKit Introduction WatchKit Extension 中的 ComplicationController ,这个文件是勾选了 <strong>Include Complication</strong> 后Xcode自动为我们生成的。它包含一个 ComplicationController 类,实现了 CLKComplicationDataSource 协议. CLKComplicationDataSource 中的方法决定了你向ClockKit提供数据的方式,这些方法里都有个handler的参数,你需要在自己的实现中回调 handle,并把自己的数据作为handler回传给ClockKit. 这是 <strong>我们和ClockKit通信</strong> 的方式:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a7005f6b64073f41939384175a9b5648.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <h2><strong>数据模版 Template:</strong></h2>    <p>Template是ClockKit为开发者定义的数据模型,但是它并 不单纯是数据模型 ,不同的模版有不同 视图布局的风格 。</p>    <p>比如,对5种Complication类型,类型和布局:</p>    <pre>  <code class="language-objectivec">typedef NS_ENUM(NSInteger, CLKComplicationFamily) {      CLKComplicationFamilyModularSmall                                                         = 0,      CLKComplicationFamilyModularLarge                                                         = 1,      CLKComplicationFamilyUtilitarianSmall                                                     = 2,      CLKComplicationFamilyUtilitarianSmallFlat   /* subset of UtilitarianSmall */              = 6,      CLKComplicationFamilyUtilitarianLarge                                                     = 3,      CLKComplicationFamilyCircularSmall                                                        = 4,      CLKComplicationFamilyExtraLarge                                                           = 7,  };</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a97c5b113537913d3952f06f36650318.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bbb8f0d70063dbdf57b9dca90f4d0069.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>比如,对于CLKComplicationTemplateModularLargeStandardBody</p>    <p>这个类,它的属性和布局对应如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/363a33d1187c0ccb03fd003d44ba85b5.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p><strong>ImageProviders|CLKTextProvider</strong></p>    <p>在 WWDC中说,这里用ImageProvider而不是UIImage, 用TextProvider而不是NSString,是因为在watch表盘的complication视图尺寸很小,防止出现文本截断等一些不好的体验,开发者使用Provider可以更好说明自己的意图 , 同时ClockKit会为我们做布局和显示的其他工作。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/698fd5e05004adb7ab84f2f0e7fcd00a.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <pre>  <code class="language-objectivec">@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);  }</code></pre>    <h3><strong>TimeLines</strong></h3>    <p>TimeLine是ClockKit抽象出来的一个时间轴。开发者的数据单元用CLKComplicationTimelineEntry 模型包装,绑定在timeline上某一点时刻. 当时间走到那一刻,ClockKit会自动将我们传给的数据内容展示在表盘。在某个时间段中,ClockKit 会一直展示上一刻展示的数据, 直到下一刻数据更新点。如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7609ad52fe96eefa63dc28e7bac446f2.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>在[11, 12) 这个域,表盘上的温度展示的是11am的时候绑定的数据55。12PM这一刻,又更新为56.</p>    <p>再比如说日历应用需要提前提醒用户"4:00-5:00要剪头发",开发者就需要把"4:00-5:00要剪头发"这个提示绑定在1PM这个时刻上,而不是直接绑在4PM这个时间点上。 如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5414e3b2b99b2f7fac215d68b5feab01.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p><strong>CLKComplicationDataSource协议类中跟timeline相关的方法</strong></p>    <pre>  <code class="language-objectivec">// 时间前进的方向,如果是回忆的时间轴,可以选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]);  }</code></pre>    <p><strong>CLKComplicationTimelineEntry</strong></p>    <p>在代码实现中,CLKComplicationTimelineEntry这个类,包含一个date属性,可以将Template数据绑定在时间轴的某一个时刻上。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b6a6f5ed3d0fba3605370ca5659485f4.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/11a937b882fe8e665788d9f4cf8afc7f.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p><strong>获取当前时刻的单条数据</strong></p>    <pre>  <code class="language-objectivec">- (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);  }</code></pre>    <p>ClockKit会回调这个方法,Extension通过调用handler,向ClockKit回传数据</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e10b22c5b5400c64292158e74b3b74e4.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p><strong>绑定多条数据</strong></p>    <p>那么如果我想给ClockKit提供其他时刻的数据呢? 这里,我们在时间轴上每15分钟绑定一条数据,我们需要用到 - (void)getTimelineEntriesForComplication:(CLKComplication *)complication afterDate:(NSDate *)date limit:(NSUInteger)limit withHandler:(void(^)(NSArray<CLKComplicationTimelineEntry *> * __nullable entries))handler 方法。</p>    <pre>  <code class="language-objectivec">- (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);  }</code></pre>    <p>如图</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e35725e3eb7dcb77b2d857b9db82774a.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>如果要绑定在当前时间之前的数据实现另一个方法</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fe419a61f798100a3be69910ea145cd0.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>现在进行真机调试,Xcode schema中选择Complication,手机和watch提前配置好,run</p>    <h2><strong>调试</strong></h2>    <ol>     <li>按下 Digital Crown 以前往表盘。</li>    </ol>    <ol>     <li style="text-align:center">用力按压显示屏。模拟器 Command+Shift+2<br> <img src="https://simg.open-open.com/show/c2198857d9946b71d3c4de4151d57d0d.png"> <p>Paste_Image.png</p> </li>     <li>向左或向右轻扫以选取某个表盘,然后轻点“自定”。 模拟器 Command+Shift+1</li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/05298f0fbc66b84b272d1829f250be53.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <ol>     <li>向左或向右轻扫以选择某个功能,然后转动 Digital Crown 对其进行更改。例如,您可以更改秒针的颜色或表盘上的标记。</li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fd98570977849b7b82b73990ffb82d7d.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <ol>     <li> <p>向左轻扫至最左边,以编辑功能栏。轻点某个功能栏以将其选中,然后转动 Digital Crown 对其进行更改。您也可以添加来自其他应用的功能栏。</p> </li>     <li> <p>完成后,按下 Digital Crown 以存储您的更改。</p> </li>    </ol>    <p> </p>    <p>参考文章</p>    <p><a href="/misc/goto?guid=4959717734982885026" rel="nofollow,noindex">https://developer.apple.com/library/content/documentation/General/Conceptual/WatchKitProgrammingGuide/ComplicationEssentials.html#//apple_ref/doc/uid/TP40014969-CH27-SW1</a></p>    <p><a href="/misc/goto?guid=4959717735086173935" rel="nofollow,noindex">https://code.tutsplus.com/tutorials/an-introduction-to-clockkit--cms-24247</a></p>    <p><a href="/misc/goto?guid=4959717735183316681" rel="nofollow,noindex">http://www.informit.com/articles/article.aspx?p=2429818</a></p>    <p>Templates 和Provider <a href="/misc/goto?guid=4959717735278928622" rel="nofollow,noindex">https://www.bignerdranch.com/blog/watchkit-2-complications/</a></p>    <p><a href="/misc/goto?guid=4959717735386498369" rel="nofollow,noindex">http://www.sneakycrab.com/blog/2015/6/10/writing-your-own-watchkit-complications</a></p>    <p> </p>    <p>来自:http://www.jianshu.com/p/56aa823dd903</p>    <p> </p>