表盘 (TSDial)
表盘模块提供完整的外设表盘管理功能,包括表盘信息获取、推送、删除、切换以及变化监听。支持内置表盘、云端表盘和自定义表盘的全生命周期管理。
前提条件
- 设备已连接并处于可用状态
- 获取
TSPeripheralDialInterface协议实例 - 对于自定义表盘操作,需要获取设备屏幕信息(
TSPeripheralScreen)以确保图片尺寸正确
数据模型
TSDialModel(表盘信息模型)
| 属性名 | 类型 | 说明 |
|---|---|---|
dialId | NSString * | 表盘唯一标识符 |
dialName | NSString * | 表盘显示名称 |
dialType | TSDialType | 表盘类型(内置/自定义/云端) |
isCurrent | BOOL | 是否为当前表盘 |
locationIndex | UInt8 | 表盘在设备上的位置索引 |
version | NSString * | 表盘版本号 |
filePath | NSString * | 表盘资源本地路径 |
TSCustomDial(自定义表盘模型)
| 属性名 | 类型 | 说明 |
|---|---|---|
dialId | NSString * | 自定义表盘唯一标识符 |
dialName | NSString * | 自定义表盘名称 |
dialType | TSCustomDialType | 自定义表盘类型(单图/多图/视频) |
templateFilePath | NSString * | 表盘模板bin文件本地路径 |
previewImageItem | TSCustomDialItem * | 预览图片项 |
resourceItems | NSArray<TSCustomDialItem *> * | 背景图片项数组 |
TSCustomDialItem(表盘项模型)
| 属性名 | 类型 | 说明 |
|---|---|---|
dialType | TSCustomDialType | 表盘项类型(单图/多图/视频) |
videoLocalPath | NSString * | 视频文件本地路径 |
resourceImage | UIImage * | 表盘背景图片 |
dialTime | TSCustomDialTime * | 时间显示配置 |
TSCustomDialTime(时间配置模型)
| 属性名 | 类型 | 说明 |
|---|---|---|
timeImage | UIImage * | 时间样式图片 |
timeImagePath | NSString * | 时间样式图片文件路径 |
timePosition | TSDialTimePosition | 时间显示位置 |
timeRect | CGRect | 时间显示区域矩形 |
timeColor | UIColor * | 时间显示颜色 |
style | TSDialTimeStyle | 时间显示样式 |
枚举与常量
TSDialType(表盘类型)
| 值 | 说明 |
|---|---|
eTSDialTypeBuiltIn (0) | 设备自带的内置表盘 |
eTSDialTypeCustomer (1) | 用户创建的自定义表盘 |
eTSDialTypeCloud (2) | 从云服务器下载的表盘 |
TSCustomDialType(自定义表盘类型)
| 值 | 说明 |
|---|---|
eTSCustomDialSingleImage (1) | 单图片自定义表盘 |
eTSCustomDialMultipleImage (2) | 多图片自定义表盘 |
eTSCustomDialVideo (3) | 视频自定义表盘 |
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 |
TSDialPushResult(表盘推送结果)
| 值 | 说明 |
|---|---|
eTSDialPushResultStart (0) | 开始 |
eTSDialPushResultProgress (0) | 推送中 |
eTSDialPushResultSuccess | 推送成功 |
eTSDialPushResultFailed | 推送失败 |
回调类型
| 回调类型 | 说明 |
|---|---|
void (^)(TSDialPushResult result, NSError *error) | 表盘操作完成回调 |
void (^)(TSDialPushResult result, NSInteger progress) | 表盘推送进度回调 |
void (^)(NSArray<TSDialModel *> *dials, NSError *error) | 表盘列表回调 |
void (^)(NSInteger remainSpace, NSError *error) | 表盘空间信息回调 |
void (^)(NSDictionary *widgets, NSError *error) | 挂件列表回调 |
接口方法
获取当前表盘
- (void)fetchCurrentDial:(void (^)(TSDialModel *_Nullable dial,
NSError *_Nullable error))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(TSDialModel *, NSError *) | 完成回调,返回当前表盘模型 |
id<TSPeripheralDialInterface> dialInterface = [peripheral getInterface:@protocol(TSPeripheralDialInterface)];
[dialInterface fetchCurrentDial:^(TSDialModel * _Nullable dial, NSError * _Nullable error) {
if (error) {
TSLog(@"获取当前表盘失败: %@", error.localizedDescription);
return;
}
TSLog(@"当前表盘: %@, 类型: %lu", dial.dialName, (unsigned long)dial.dialType);
}];
获取所有表盘
- (void)fetchAllDials:(TSDialListBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(NSArray<TSDialModel *> *, NSError *) | 完成回调,返回所有表盘模型数组 |
[dialInterface fetchAllDials:^(NSArray<TSDialModel *> * _Nullable dials, NSError * _Nullable error) {
if (error) {
TSLog(@"获取表盘列表失败: %@", error.localizedDescription);
return;
}
TSLog(@"设备上共有 %lu 个表盘", (unsigned long)dials.count);
for (TSDialModel *dial in dials) {
TSLog(@"表盘: %@, 类型: %lu, 当前: %@", dial.dialName, (unsigned long)dial.dialType, dial.isCurrent ? @"是" : @"否");
}
}];
切换表盘
- (void)switchToDial:(TSDialModel *)dial
completion:(nullable void(^)(BOOL isSuccess, NSError *_Nullable error))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
dial | TSDialModel * | 要切换的表盘模型 |
completion | void (^)(BOOL, NSError *) | 完成回调 |
TSDialModel *targetDial = [[TSDialModel alloc] init];
targetDial.dialId = @"dial_001";
[dialInterface switchToDial:targetDial completion:^(BOOL isSuccess, NSError * _Nullable error) {
if (isSuccess) {
TSLog(@"表盘切换成功");
} else {
TSLog(@"表盘切换失败: %@", error.localizedDescription);
}
}];
生成自定义表盘ID
- (nonnull NSString *)generateCustomDialIdWithType:(TSCustomDialType)dialType;
| 参数 | 类型 | 说明 |
|---|---|---|
dialType | TSCustomDialType | 自定义表盘类型 |
| 返回值 | 类型 | 说明 |
|---|---|---|
NSString * | 生成的唯一表盘ID字符串 |
NSString *customDialId = [dialInterface generateCustomDialIdWithType:eTSCustomDialSingleImage];
TSLog(@"生成的自定义表盘ID: %@", customDialId);
推送云端表盘
- (void)installDownloadedCloudDial:(TSDialModel *)dial
progressBlock:(nullable TSDialProgressBlock)progressBlock
completion:(nullable TSDialCompletionBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
dial | TSDialModel * | 云端表盘模型 |
progressBlock | void (^)(TSDialPushResult, NSInteger) | 进度回调 |
completion | void (^)(TSDialPushResult, NSError *) | 完成回调 |
TSDialModel *cloudDial = [[TSDialModel alloc] init];
cloudDial.dialId = @"cloud_dial_001";
cloudDial.dialType = eTSDialTypeCloud;
[dialInterface installDownloadedCloudDial:cloudDial
progressBlock:^(TSDialPushResult result, NSInteger progress) {
TSLog(@"推送进度: %ld%%", (long)progress);
} completion:^(TSDialPushResult result, NSError * _Nullable error) {
if (result == eTSDialPushResultSuccess) {
TSLog(@"云端表盘推送成功");
} else if (result == eTSDialPushResultFailed) {
TSLog(@"云端表盘推送失败: %@", error.localizedDescription);
}
}];
推送自定义表盘
- (void)installCustomDial:(TSCustomDial *)customDial
progressBlock:(nullable TSDialProgressBlock)progressBlock
completion:(nullable TSDialCompletionBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
customDial | TSCustomDial * | 自定义表盘模型 |
progressBlock | void (^)(TSDialPushResult, NSInteger) | 进度回调 |
completion | void (^)(TSDialPushResult, NSError *) | 完成回调 |
TSCustomDial *customDial = [[TSCustomDial alloc] init];
customDial.dialId = [dialInterface generateCustomDialIdWithType:eTSCustomDialSingleImage];
customDial.dialName = @"我的自定义表盘";
customDial.dialType = eTSCustomDialSingleImage;
customDial.templateFilePath = @"/path/to/template.bin";
TSCustomDialItem *item = [[TSCustomDialItem alloc] init];
item.dialType = eTSCustomDialSingleImage;
item.resourceImage = [UIImage imageNamed:@"dial_background"];
item.dialTime = [[TSCustomDialTime alloc] init];
item.dialTime.timePosition = eTSDialTimePositionTop;
customDial.resourceItems = @[item];
[dialInterface installCustomDial:customDial
progressBlock:^(TSDialPushResult result, NSInteger progress) {
TSLog(@"自定义表盘推送进度: %ld%%", (long)progress);
} completion:^(TSDialPushResult result, NSError * _Nullable error) {
if (result == eTSDialPushResultSuccess) {
TSLog(@"自定义表盘推送成功");
} else if (result == eTSDialPushResultFailed) {
TSLog(@"自定义表盘推送失败: %@", error.localizedDescription);
}
}];
删除表盘
- (void)deleteDial:(TSDialModel *)dial
completion:(nullable void(^)(BOOL isSuccess, NSError *_Nullable error))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
dial | TSDialModel * | 要删除的表盘模型 |
completion | void (^)(BOOL, NSError *) | 完成回调 |
TSDialModel *dialToDelete = [[TSDialModel alloc] init];
dialToDelete.dialId = @"custom_dial_001";
[dialInterface deleteDial:dialToDelete completion:^(BOOL isSuccess, NSError * _Nullable error) {
if (isSuccess) {
TSLog(@"表盘删除成功");
} else {
TSLog(@"表盘删除失败: %@", error.localizedDescription);
}
}];
获取表盘剩余存储空间
- (void)fetchWatchFaceRemainingStorageSpace:(nullable TSDialSpaceBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(NSInteger, NSError *) | 完成回调,返回剩余空间(字节) |
[dialInterface fetchWatchFaceRemainingStorageSpace:^(NSInteger remainSpace, NSError * _Nullable error) {
if (error) {
TSLog(@"获取剩余空间失败: %@", error.localizedDescription);
return;
}
TSLog(@"表盘剩余空间: %ld 字节", (long)remainSpace);
}];
注册表盘变化监听
- (void)registerDialDidChangedBlock:(void (^)(NSArray<TSDialModel *> *_Nullable allDials))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(NSArray<TSDialModel *> *) | 表盘变化回调 |
[dialInterface registerDialDidChangedBlock:^(NSArray<TSDialModel *> * _Nullable allDials) {
if (allDials == nil) {
TSLog(@"无法获取表盘信息");
return;
}
TSLog(@"表盘已变化,当前共有 %lu 个表盘", (unsigned long)allDials.count);
for (TSDialModel *dial in allDials) {
if (dial.isCurrent) {
TSLog(@"当前表盘: %@", dial.dialName);
}
}
}];
取消表盘推送
- (void)cancelPushDial:(TSCompletionBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(BOOL, NSError *) | 完成回调 |
[dialInterface cancelPushDial:^(BOOL isSuccess, NSError * _Nullable error) {
if (isSuccess) {
TSLog(@"表盘推送已取消");
} else {
TSLog(@"取消表盘推送失败: %@", error.localizedDescription);
}
}];
获取内置表盘最大数量
- (NSInteger)maxInnerDialCount;
| 返回值 | 类型 | 说明 |
|---|---|---|
NSInteger | 设备支持的内置表盘最大数量 |
NSInteger maxCount = [dialInterface maxInnerDialCount];
TSLog(@"设备支持的内置表盘最大数量: %ld", (long)maxCount);
获取可推送表盘最大数量
- (NSInteger)maxCanPushDialCount;
| 返回值 | 类型 | 说明 |
|---|---|---|
NSInteger | 设备可以存储的表盘最大数量 |
NSInteger maxPushCount = [dialInterface maxCanPushDialCount];
TSLog(@"设备可推送的表盘最大数量: %ld", (long)maxPushCount);
检查幻灯片表盘支持
- (BOOL)isSupportSlideshowDial;
| 返回值 | 类型 | 说明 |
|---|---|---|
BOOL | 是否支持幻灯片表盘 |
if ([dialInterface isSupportSlideshowDial]) {
TSLog(@"设备支持幻灯片表盘");
} else {
TSLog(@"设备不支持幻灯片表盘");
}
检查视频表盘支持
- (BOOL)isSupportVideoDial;
| 返回值 | 类型 | 说明 |
|---|---|---|
BOOL | 是否支持视频表盘 |
if ([dialInterface isSupportVideoDial]) {
TSLog(@"设备支持视频表盘");
} else {
TSLog(@"设备不支持视频表盘");
}
检查表盘组件支持
- (BOOL)isSupportDialComponent;
| 返回值 | 类型 | 说明 |
|---|---|---|
BOOL | 是否支持表盘组件 |
if ([dialInterface isSupportDialComponent]) {
TSLog(@"设备支持表盘组件");
}
获取视频表盘最大时长
- (NSInteger)maxVideoDialDuration;
| 返回值 | 类型 | 说明 |
|---|---|---|
NSInteger | 最大视频时长(秒) |
NSInteger maxDuration = [dialInterface maxVideoDialDuration];
TSLog(@"视频表盘最大时长: %ld 秒", (long)maxDuration);
获取设备支持的挂件列表
- (void)requestSupportWidgetsFromPeripheralCompletion:(TSDialWidgetsBlock)completion;
| 参数 | 类型 | 说明 |
|---|---|---|
completion | void (^)(NSDictionary *, NSError *) | 完成回调,返回挂件信息字典 |
[dialInterface requestSupportWidgetsFromPeripheralCompletion:^(NSDictionary * _Nullable widgets, NSError * _Nullable error) {
if (error) {
TSLog(@"获取挂件列表失败: %@", error.localizedDescription);
return;
}
if (widgets) {
TSLog(@"设备支持的挂件: %@", widgets);
}
}];
生成表盘预览图
- (void)previewImageWith:(UIImage *)originImage
timeImage:(UIImage *)timeImage
timePosition:(TSDialTimePosition)timePosition
maxKBSize:(CGFloat)maxKBSize
completion:(void (^)(UIImage *_Nullable, NSError *_Nullable))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
originImage | UIImage * | 背景图片 |
timeImage | UIImage * | 时间显示图片 |
timePosition | TSDialTimePosition | 时间位置 |
maxKBSize | CGFloat | 最大文件大小(KB) |
completion | void (^)(UIImage *, NSError *) | 完成回调 |
UIImage *background = [UIImage imageNamed:@"dial_bg"];
UIImage *timeImg = [UIImage imageNamed:@"time_style"];
[dialInterface previewImageWith:background
timeImage:timeImg
timePosition:eTSDialTimePositionTop
maxKBSize:300
completion:^(UIImage * _Nullable previewImage, NSError * _Nullable error) {
if (error) {
TSLog(@"生成预览图失败: %@", error.localizedDescription);
return;
}
TSLog(@"预览图生成成功");
}];
根据表盘项生成预览图
- (void)previewImageWithDialItem:(TSCustomDialItem *)dialItem
maxKBSize:(CGFloat)maxKBSize
completion:(void (^)(UIImage *_Nullable, NSError *_Nullable))completion;
| 参数 | 类型 | 说明 |
|---|---|---|
dialItem | TSCustomDialItem * | 自定义表盘项 |
maxKBSize | CGFloat | 最大文件大小(KB) |
completion | void (^)(UIImage *, NSError *) | 完成回调 |
TSCustomDialItem *item = [[TSCustomDialItem alloc] init];
item.dialType = eTSCustomDialSingleImage;
item.resourceImage = [UIImage imageNamed:@"dial_background"];
item.dialTime = [[TSCustomDialTime alloc] init];
item.dialTime.timeImage = [UIImage imageNamed:@"time_style"];
item.dialTime.timePosition = eTSDialTimePositionTop;
[dialInterface previewImageWithDialItem:item
maxKBSize:300
completion:^(UIImage * _Nullable previewImage, NSError * _Nullable error) {
if (error) {
TSLog(@"生成预览图失败: %@", error.localizedDescription);
return;
}
TSLog(@"预览图生成成功");
}];
注意事项
-
表盘模型区分:使用
TSDialModel处理设备表盘信息和云端表盘操作,使用TSCustomDial创建和推送自定义表盘。 -
图片尺寸要求严格:背景图片必须与设备屏幕尺寸完全匹配(通过
TSPeripheralScreen.screenSize获取),预览图片必须与预览尺寸匹配(通过TSPeripheralScreen.dialPreviewSize获取)。 -
内置表盘不可删除:只能删除自定义表盘和云端表盘,内置表盘无法通过接口删除。
-
推送操作异步执行:表盘推送是异步操作,完成回调会被多次调用,需要根据
TSDialPushResult判断推送状态。 -
表盘变化监听:同时只能注册一个表盘变化回调,新的注册会替换之前的回调。
-
存储空间检查:推送新表盘前应检查剩余存储空间,确保有足够空间。
-
视频表盘限制:视频表盘有最大时长限制,需先检查设备是否支持视频表盘。
-
时间位置优先级:
timeRect优先于timePosition使用,如果设置了有效的timeRect则忽略timePosition。