添加 Apple Watch 4 新的图形复杂功能(watchOS 5.0+)并防止Apple Watch Series 1、2 和 3 崩溃。
重要提示:来自 Zoltan 的代码将在 Apple Watch 系列 3 或更低版本上崩溃。
根据Apple 文档,新的图形复杂功能需要 watchOS 5.0 或更高版本以及Watch Series 4 或更高版本:
注意支持图形模板的表盘仅适用于 Apple Watch Series 4 或更新机型。
这意味着带有 watchOS 5 或 6 的 Watch Series 3(由于if #available(watchOSApplicationExtension 5.0, *)
)将尝试从资产目录加载复杂功能图像。但是,由于默认情况下启用了App Thinning,Watch Series 3 的二进制文件中没有图像,因此以下行将使应用程序崩溃:
let image = UIImage(named: "Complication/Graphic Corner")!
当我们在 Xcode 中发现数以千计的崩溃报告时,我们发现了这一点:
- 打开 Xcode
- 窗口 -> 组织者
- 选择选项卡“崩溃”
- 选择 App Store 版本
我们为我们支持的 2 种图形并发症中的每一种都找到了崩溃报告,所有这些都与 watchOS 5 或 6 以及 Watch Series 2 或 3 一起使用,例如:
解决方案
在 IF 语句中嵌入图形资源的加载并nil
作为模板返回,因此它不会在没有资源的设备上崩溃。
上面来自 Zoltan的示例将是:
case .graphicCorner:
if #available(watchOSApplicationExtension 5.0, *) {
let template = CLKComplicationTemplateGraphicCornerCircularImage()
if let image = UIImage(named: "Complication/Graphic Corner") {
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else {
handler(nil)
}
} else {
handler(nil)
}
为了高效且可维护的代码,我们创建了一个可重用的函数templateForComplication()
,用于所有 3 个强制委托函数:
class ComplicationController: NSObject, CLKComplicationDataSource {
// MARK: Mandatory Delegate Methods
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
// Turn off time travelling:
handler([])
}
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
let template = templateForComplication(complication: complication)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template!)
handler(timelineEntry)
}
func getPlaceholderTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
handler(templateForComplication(complication: complication))
}
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
handler(templateForComplication(complication: complication))
}
// MARK: Helper Methods
private func templateForComplication(complication: CLKComplication) -> CLKComplicationTemplate? {
// Init default output:
var template: CLKComplicationTemplate? = nil
// Graphic Complications are only availably since watchOS 5.0:
if #available(watchOSApplicationExtension 5.0, *) {
// NOTE: Watch faces that support graphic templates are available only on Apple Watch Series 4 or later. So the binary on older devices (e.g. Watch Series 3) will not contain the images.
if complication.family == .graphicCircular {
let imageTemplate = CLKComplicationTemplateGraphicCircularImage()
// Check if asset exists, to prevent crash on non-supported devices:
if let fullColorImage = UIImage(named: "Complication/Graphic Circular") {
let imageProvider = CLKFullColorImageProvider.init(fullColorImage: fullColorImage)
imageTemplate.imageProvider = imageProvider
template = imageTemplate
}
}
else if complication.family == .graphicCorner {
let imageTemplate = CLKComplicationTemplateGraphicCornerCircularImage()
// Check if asset exists, to prevent crash on non-supported devices:
if let fullColorImage = UIImage(named: "Complication/Graphic Corner") {
let imageProvider = CLKFullColorImageProvider.init(fullColorImage: fullColorImage)
imageTemplate.imageProvider = imageProvider
template = imageTemplate
}
}
}
// For all watchOS versions:
if complication.family == .circularSmall {
let imageTemplate = CLKComplicationTemplateCircularSmallSimpleImage()
let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Circular")!)
imageProvider.tintColor = UIColor.blue
imageTemplate.imageProvider = imageProvider
template = imageTemplate
}
else if complication.family == .modularSmall {
let imageTemplate = CLKComplicationTemplateModularSmallSimpleImage()
let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Modular")!)
imageProvider.tintColor = UIColor.blue
imageTemplate.imageProvider = imageProvider
template = imageTemplate
}
else if complication.family == .utilitarianSmall {
let imageTemplate = CLKComplicationTemplateUtilitarianSmallSquare()
let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Complication/Utilitarian")!)
imageProvider.tintColor = UIColor.blue
imageTemplate.imageProvider = imageProvider
template = imageTemplate
}
return template
}
}