跳到主要内容

表盘 (TSDial)

表盘模块提供完整的外设表盘管理功能,包括表盘信息获取、推送、删除、切换以及变化监听。支持内置表盘、云端表盘和自定义表盘的全生命周期管理。

前提条件​

  • 设备已连接并处于可用状态
  • 获取 TSPeripheralDialInterface 协议实例
  • 对于自定义表盘操作,需要获取设备屏幕信息(TSPeripheralScreen)以确保图片尺寸正确

数据模型​

TSDialModel(表盘信息模型)​

属性名类型说明
dialIdNSString *表盘唯一标识符
dialNameNSString *表盘显示名称
dialTypeTSDialType表盘类型(内置/自定义/云端)
isCurrentBOOL是否为当前表盘
locationIndexUInt8表盘在设备上的位置索引
versionNSString *表盘版本号

TSDialTime(时间配置)​

属性类型说明
timeImageUIImage *时间样式图片,优先于路径
timeImagePathNSString *本地或 Bundle 图片路径
timePositionTSDialTimePositiontimeRect 未指定时的备用位置
timeRectCGRect背景图像素坐标中的时间区域
timeColorUIColor *单色时间图片的可选着色
styleTSDialTimeStyle内置时间样式标识

TSDialDraft 与 TSDialDraftItem​

TSDialDraft 通过 initWithDraftType:templateFilePath:items: 创建。draftType、templateFilePath 和 items 为只读;dialId、previewImage 和 multiplePlayIntervalMillis 为可选输入。资源项使用 itemWithImage:time: 或 itemWithVideoFilePath:time: 创建,图片尺寸必须匹配设备屏幕像素尺寸。TSDialTime 为不可变对象,须使用指定初始化方法创建。

TSDialDanMuItem​

通过 initWithImage: 创建滚动图片。仅 TSDialDraftTypeDanMu 使用,背景资源项的 danMuItems 至少包含一项。静态图片必填;animationFilePath 可选,指向本地可读 GIF。walkSpeed 为有限正数,单位像素/秒,默认 60;leftToRight 默认 NO。

imageX、imageY、animationX、animationY 使用包含 anchor 和 offset 的 TSDialDanMuCoordinate,通过 TSDialDanMuCoordinateMake(anchor, offset) 创建。偏移量为 -524288…524287 的有符号像素值;绝对坐标直接使用偏移量,其他锚点先按内容原始尺寸对齐,再加偏移量。

TSDialDanMuAnchor值含义
TSDialDanMuAnchorAbsolute0左上角绝对坐标
TSDialDanMuAnchorStart1横轴左对齐、纵轴顶对齐
TSDialDanMuAnchorCenter2当前轴居中
TSDialDanMuAnchorEnd3横轴右对齐、纵轴底对齐

当前统一表盘模型​

模型关键属性说明
TSDialCapabilitysupportsCustom, supportsVideo, maxVideoDuration, supportsSlideshow, supportsDanMu, maxSlideshowImages, colorTintable, supportsComponent, shouldRenderTimeInPreview, maxInstallCount, maxInnerCount, screenWidth, screenHeight, deviceCornerRadius, previewWidth, previewHeight, previewCornerRadius, shape只读的当前设备静态能力快照
TSStorageSpaceavailable, total表盘存储空间,单位字节;total == 0 表示未知
TSDialDraftdialId, draftType, templateFilePath, items, previewImage, multiplePlayIntervalMillis自定义表盘构建输入
TSDialDraftItemitemType, image, videoFilePath, time, danMuItems单张图片或视频素材
TSDialDanMuItemimage, imageX, imageY, walkSpeed, leftToRight, animationFilePath, animationX, animationY一条静态图片弹幕及可选关联 GIF
TSDialArtifactdialId, dialType, filePath唯一安装输入,可由自定义造包生成,或使用已下载的云端包构造
TSComposePreviewInputbackgroundImage, time, maxKBSize, keepTransparentBackground预览合成输入
TSCustomDialStyleOptionstyle, previewImageURL, size可用时间样式
TSCustomDialPositionOptionposition, frame可用时间位置
TSCustomDialStyleConstraintscreenSize, screenShape, styles, positions, allowColorTintProvider 无关的自定义样式约束

构建与预览规则​

构建前先查询 dialCapability;设备缓存信息不可用时可能返回 nil。maxInstallCount == -1 表示无限制,0 表示不支持安装云端/自定义表盘。maxSlideshowImages == 0 表示无明确图片数量上限。选择草稿类型前应检查相应能力标志。

单图、视频和弹幕草稿必须只有一个资源项,多图草稿需要一个或多个图片项。除弹幕外均需提供时间配置。模板路径为 nil 时由 Provider 解析兼容模板。multiplePlayIntervalMillis 非正数使用 Provider 默认值,正数在构建时量化;视频和 GIF 源文件在构建完成前须保持可读且不变。

shouldRenderTimeInPreview 表示 Provider 合成安装预览图时是否叠加时间,与 supportsComponent 独立;仍需时间配置,不控制手表实际时间显示,也不修改 App 提供的成品预览图。自行设置 draft.previewImage 时,其像素尺寸必须等于 TSPeripheralScreen.dialPreviewSize。

fetchDialStorage: 仅返回表盘区域空间;total 未知时仍可使用 available。TSStorageSpace 提供 used、usagePercentage、hasEnoughSpaceForSize: 和 formattedStorageInfo。总量未知时,已用空间和使用率返回 0,不代表实际使用情况。

枚举与常量​

TSDialType(表盘类型)​

值说明
eTSDialTypeBuiltIn (0)设备自带的内置表盘
eTSDialTypeCustomer (1)用户创建的自定义表盘
eTSDialTypeCloud (2)从云服务器下载的表盘

TSDialDraftType(表盘草稿类型)​

值说明
TSDialDraftTypeSingleImage (1)单图片自定义表盘
TSDialDraftTypeMultipleImage (2)多图片自定义表盘
TSDialDraftTypeVideo (3)视频自定义表盘
TSDialDraftTypeDanMu (4)弹幕图片表盘

TSDialTimePosition(时间显示位置)​

值说明
eTSDialTimePositionTop (0)上方
eTSDialTimePositionBottom (1)下方
eTSDialTimePositionLeft (2)左方
eTSDialTimePositionRight (3)右方
eTSDialTimePositionTopLeft (4)左上
eTSDialTimePositionBottomLeft (5)左下
eTSDialTimePositionTopRight (6)右上
eTSDialTimePositionBottomRight (7)右下
eTSDialTimePositionCenter (8)中间

TSDialTimeStyle(时间显示样式)​

值说明
eTSDialTimeStyleNone (0)无样式
eTSDialTimeStyle1 (1)时间样式1
eTSDialTimeStyle2 (2)时间样式2
eTSDialTimeStyle3 (3)时间样式3
eTSDialTimeStyle4 (4)时间样式4
eTSDialTimeStyle5 (5)时间样式5
eTSDialTimeStyle6 (6)时间样式6
eTSDialTimeStyle7 (7)时间样式7

TSDialInstallResult(表盘安装结果)​

值说明
eTSDialInstallResultStart (0)开始
eTSDialInstallResultProgress (1)安装中
eTSDialInstallResultSuccess (2)安装成功
eTSDialInstallResultFailed (3)安装失败
eTSDialInstallResultCompleted (4)安装流程结束

构建类型与错误​

  • TSDialDraftType:TSDialDraftTypeSingleImage、TSDialDraftTypeMultipleImage、TSDialDraftTypeVideo、TSDialDraftTypeDanMu。
  • TSDialDraftItemType:TSDialDraftItemTypeImage、TSDialDraftItemTypeVideo。
  • TSDialErrorCode:TSDialErrorInvalidDraftType、TSDialErrorMissingTemplateFilePath、TSDialErrorTemplateFileNotFound、TSDialErrorEmptyDraftItems、TSDialErrorInvalidDraftItemCount、TSDialErrorInvalidDraftItemType、TSDialErrorMissingDraftItemTime、TSDialErrorMissingImageResource、TSDialErrorMissingVideoFilePath、TSDialErrorVideoFileNotFound、TSDialErrorInvalidScreenSize、TSDialErrorImageSizeMismatch、TSDialErrorMissingArtifactDialId、TSDialErrorMissingArtifactFilePath、TSDialErrorArtifactFileNotFound、TSDialErrorMissingPreviewBackgroundImage、TSDialErrorInvalidArtifactDialType, TSDialErrorInvalidDanMuItem。

回调类型​

回调类型说明
TSDialInstallCompletionBlock必传的安装最终结果与错误信息;须根据 result 判断结果
TSDialInstallProgressBlock安装阶段与 0–100 进度
TSDialListBlock表盘列表与错误信息
TSDialWidgetsBlockFw 系列设备支持的挂件字典与错误信息
TSCustomDialStyleConstraintBlock自定义表盘样式约束与错误信息

当前接口方法​

能力、样式与查询​

- (nullable TSDialCapability *)dialCapability;
- (BOOL)isSupportCustomDialStyleConstraint;
- (void)fetchCustomDialStyleConstraint:(TSCustomDialStyleConstraintBlock)completion;
- (void)fetchAllDials:(TSDialListBlock)completion;
- (void)fetchCurrentDial:(void (^)(TSDialModel *_Nullable dial, NSError *_Nullable error))completion;
- (void)fetchDialStorage:(void (^)(TSStorageSpace *_Nullable storage, NSError *_Nullable error))completion;

能力快照来自本地设备信息;样式约束和列表/当前表盘/存储通过异步回调返回。

选择、构建与预览​

- (void)selectDial:(NSString *)dialId completion:(TSCompletionBlock)completion;
- (void)buildDialWithDraft:(TSDialDraft *)draft
completion:(void (^)(TSDialArtifact *_Nullable artifact, NSError *_Nullable error))completion;
- (void)composeDialPreview:(TSComposePreviewInput *)input
completion:(void (^)(UIImage *_Nullable previewImage, NSError *_Nullable error))completion;

App 负责素材裁剪、视频截取与屏幕尺寸适配;构建成功后把 TSDialArtifact 交给安装接口。

安装、卸载与事件​

- (void)installDial:(TSDialArtifact *)artifact
progressBlock:(nullable TSDialInstallProgressBlock)progressBlock
completion:(TSDialInstallCompletionBlock)completion;
- (void)cancelDialInstall:(TSCompletionBlock)completion;
- (void)uninstallDial:(NSString *)dialId completion:(TSCompletionBlock)completion;
- (void)registerDialListDidChangeHandler:(void (^)(NSArray<TSDialModel *> *_Nullable allDials,
NSError *_Nullable error))handler;
- (void)fetchSupportedDialWidgets:(TSDialWidgetsBlock)completion;

安装进度范围为 0–100;内置表盘不能卸载;列表监听仅保留最后一次注册。

现有接口示例​

以下片段在 SDK 初始化并连接设备后,按操作分别放入 App 方法中执行。资源名、路径和 ID 需替换为实际值。先获取接口实例:

id<TSPeripheralDialInterface> dialInterface = [TopStepComKit sharedInstance].dial;
if (dialInterface == nil) {
TSLog(@"[Dial] 表盘接口不可用");
return;
}

查询设备能力与表盘列表​

TSDialCapability *capability = [dialInterface dialCapability];
if (capability == nil) {
TSLog(@"[Dial] 设备能力信息尚未就绪");
return;
}
TSLog(@"支持自定义:%@,视频:%@,最大视频时长:%ld秒",
capability.supportsCustom ? @"是" : @"否",
capability.supportsVideo ? @"是" : @"否",
(long)capability.maxVideoDuration);

[dialInterface fetchAllDials:^(NSArray<TSDialModel *> *dials, NSError *error) {
if (error) {
TSLog(@"[Dial] 获取表盘列表失败: %@", error.localizedDescription);
return;
}
for (TSDialModel *dial in dials) {
TSLog(@"%@ %@", dial.dialName, dial.isCurrent ? @"(当前)" : @"");
}
}];
[dialInterface fetchCurrentDial:^(TSDialModel *dial, NSError *error) {
if (!error && dial) {
TSLog(@"[Dial] 当前表盘: %@", dial.dialName);
}
}];

查询样式约束和存储空间​

if ([dialInterface isSupportCustomDialStyleConstraint]) {
[dialInterface fetchCustomDialStyleConstraint:^(TSCustomDialStyleConstraint *constraint,
NSError *error) {
if (!error && constraint) {
TSLog(@"[Dial] 可用时间样式: %lu", (unsigned long)constraint.styles.count);
}
}];
}
[dialInterface fetchDialStorage:^(TSStorageSpace *storage, NSError *error) {
if (error || !storage) {
TSLog(@"[Dial] 查询表盘空间失败: %@", error.localizedDescription);
return;
}
TSLog(@"[Dial] 可用空间: %llu 字节", storage.available);
if (storage.total > 0) {
TSLog(@"[Dial] 总空间: %llu 字节", storage.total);
}
}];

构建并预览单图表盘​

UIImage *image = [UIImage imageNamed:@"dial_background"];
if (!image || ![dialInterface dialCapability].supportsCustom) {
TSLog(@"[Dial] 请提供屏幕尺寸的图片,并确认设备支持自定义表盘");
return;
}
TSDialTime *time = [[TSDialTime alloc] initWithTimeImage:[UIImage imageNamed:@"time_style"]
timeImagePath:nil
timePosition:eTSDialTimePositionTop
timeRect:CGRectZero
timeColor:nil
style:eTSDialTimeStyle1];
TSDialDraftItem *item = [TSDialDraftItem itemWithImage:image time:time];
TSDialDraft *draft = [[TSDialDraft alloc] initWithDraftType:TSDialDraftTypeSingleImage
templateFilePath:nil
items:@[item]];
[dialInterface composeDialPreview:[[TSComposePreviewInput alloc]
initWithBackgroundImage:image time:time maxKBSize:300 keepTransparentBackground:NO]
completion:^(UIImage *preview, NSError *error) {
if (!error && preview) {
TSLog(@"[Dial] 预览图已生成: %@", preview);
}
}];
[dialInterface buildDialWithDraft:draft completion:^(TSDialArtifact *artifact, NSError *error) {
if (error || !artifact) {
TSLog(@"[Dial] 造包失败: %@", error.localizedDescription);
return;
}
[dialInterface installDial:artifact progressBlock:^(TSDialInstallResult result, NSInteger progress) {
TSLog(@"安装进度: %ld%%", (long)progress);
} completion:^(TSDialInstallResult result, NSError *error) {
TSLog(@"[Dial] 安装结果: %ld,错误: %@", (long)result, error.localizedDescription);
}];
}];

构建视频表盘​

复用单图示例中的 time 配置。视频需匹配设备屏幕尺寸,时长不超过 maxVideoDuration,构建完成前保持文件可读。

if (![dialInterface dialCapability].supportsVideo) {
TSLog(@"[Dial] 设备不支持视频表盘");
return;
}
TSDialDraftItem *item = [TSDialDraftItem itemWithVideoFilePath:@"/path/to/watchface.mp4"
time:time];
TSDialDraft *draft = [[TSDialDraft alloc] initWithDraftType:TSDialDraftTypeVideo
templateFilePath:nil
items:@[item]];
[dialInterface buildDialWithDraft:draft completion:^(TSDialArtifact *artifact, NSError *error) {
if (error || !artifact) {
TSLog(@"[Dial] 造包失败: %@", error.localizedDescription);
return;
}
[dialInterface installDial:artifact progressBlock:nil completion:^(TSDialInstallResult result, NSError *installError) {
TSLog(@"[Dial] 视频安装结果: %ld,错误: %@", (long)result, installError.localizedDescription);
}];
}];

切换、取消和卸载表盘​

下列为独立操作,不应在发起安装后连续执行。切换 ID 来自 fetchAllDials:,仅云端或自定义表盘允许卸载。

[dialInterface selectDial:@"dial-id" completion:^(BOOL success, NSError *error) {
TSLog(@"切换结果: %@", success ? @"成功" : error.localizedDescription);
}];
[dialInterface cancelDialInstall:^(BOOL success, NSError *error) {
TSLog(@"取消安装: %@", success ? @"成功" : error.localizedDescription);
}];
[dialInterface uninstallDial:@"custom-dial-id" completion:^(BOOL success, NSError *error) {
TSLog(@"卸载结果: %@", success ? @"成功" : error.localizedDescription);
}];

监听表盘列表变化与获取挂件​

[dialInterface registerDialListDidChangeHandler:^(NSArray<TSDialModel *> *dials, NSError *error) {
if (!error && dials) {
TSLog(@"[Dial] 表盘列表已更新,共 %lu 个", (unsigned long)dials.count);
}
}];
[dialInterface fetchSupportedDialWidgets:^(NSDictionary *widgets, NSError *error) {
if (!error && widgets) {
TSLog(@"[Dial] 支持的挂件: %@", widgets);
}
}];

注意事项​

  1. TSDialDraft、TSDialDraftItem 和 TSDialTime 是当前自定义表盘构建入口;图片像素尺寸必须匹配设备屏幕尺寸。
  2. TSStorageSpace 的 available 和 total 均以字节计;安装前检查 available,total == 0 表示未知。
  3. 单图、视频和弹幕草稿需要一个资源项,多图草稿可包含多个图片项;弹幕草稿还必须提供非空 danMuItems。
  4. 安装回调中的进度范围为 0–100;安装完成后可在回调中刷新表盘列表。
  5. 内置表盘不能卸载;registerDialListDidChangeHandler: 传入新回调会替换之前的监听,事件不区分安装、卸载或切换来源。