WatchOS3 Complication, который запускает приложение
Я хотел бы создать услугу для watchOS 3, которая просто запустит мое приложение. Я использовал XCode для создания ComplicationController:
class ComplicationController: NSObject, CLKComplicationDataSource
{
// MARK: - Timeline Configuration
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
handler([.forward, .backward])
}
func getTimelineStartDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) {
handler(nil)
}
func getTimelineEndDate(for complication: CLKComplication, withHandler handler: @escaping (Date?) -> Void) {
handler(nil)
}
func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) {
handler(.showOnLockScreen)
}
// MARK: - Timeline Population
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK: - Placeholder Templates
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
handler(nil)
}
}
и добавлены изображения для циркулярных, модульных и утилитарных активов. Но когда я запускаю приложение Watch, я не могу выбрать свои сложности для лица Watch. Что мне еще нужно сделать?
Спасибо
Greg
4 ответа
Эти изменения кода необходимы:
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void)
{
handler([])
}
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void)
{
if complication.family == .circularSmall
{
let template = CLKComplicationTemplateCircularSmallRingImage()
template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Circular")!)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else if complication.family == .utilitarianSmall
{
let template = CLKComplicationTemplateUtilitarianSmallRingImage()
template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Utilitarian")!)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else if complication.family == .modularSmall
{
let template = CLKComplicationTemplateModularSmallRingImage()
template.imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "Modular")!)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else {
handler(nil)
}
}
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void)
{
switch complication.family
{
case .circularSmall:
let image: UIImage = UIImage(named: "Circular")!
let template = CLKComplicationTemplateCircularSmallSimpleImage()
template.imageProvider = CLKImageProvider(onePieceImage: image)
handler(template)
case .utilitarianSmall:
let image: UIImage = UIImage(named: "Utilitarian")!
let template = CLKComplicationTemplateUtilitarianSmallSquare()
template.imageProvider = CLKImageProvider(onePieceImage: image)
handler(template)
case .modularSmall:
let image: UIImage = UIImage(named: "Modular")!
let template = CLKComplicationTemplateModularSmallSimpleImage()
template.imageProvider = CLKImageProvider(onePieceImage: image)
handler(template)
default:
handler(nil)
}
}
Кроме того, вы должны предоставить изображения в качестве активов в расширении.
Новые графические усложнения Apple Watch 4 выглядят так:
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
switch complication.family {
case .graphicCorner:
if #available(watchOSApplicationExtension 5.0, *) {
let template = CLKComplicationTemplateGraphicCornerCircularImage()
let image = UIImage(named: "Complication/Graphic Corner")!
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else {
handler(nil)
}
case .graphicCircular:
if #available(watchOSApplicationExtension 5.0, *) {
let template = CLKComplicationTemplateGraphicCircularImage()
let image = UIImage(named: "Complication/Graphic Circular")!
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
} else {
handler(nil)
}
default:
handler(nil)
}
}
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
switch complication.family {
case .graphicCorner:
if #available(watchOSApplicationExtension 5.0, *) {
let template = CLKComplicationTemplateGraphicCornerCircularImage()
let image = UIImage(named: "Complication/Graphic Corner")!
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
handler(template)
} else {
handler(nil)
}
case .graphicCircular:
if #available(watchOSApplicationExtension 5.0, *) {
let template = CLKComplicationTemplateGraphicCircularImage()
let image = UIImage(named: "Complication/Graphic Circular")!
template.imageProvider = CLKFullColorImageProvider(fullColorImage: image)
handler(template)
} else {
handler(nil)
}
default:
handler(nil)
}
}
Add Apple Watch 4 new graphic complications (watchOS 5.0+) and prevent crash on Apple Watch Series 1, 2 and 3.
Important: the code from Zoltan will crash on Apple Watch series 3 or lower.
According to the Apple documentation, the new graphic complications require watchOS 5.0 or higher and a Watch Series 4 or later:
Note Watch faces that support graphic templates are available only on Apple Watch Series 4 or later.
This means that a Watch Series 3 with watchOS 5 or 6 (due to if #available(watchOSApplicationExtension 5.0, *)
) will attempt to load the complication image from the asset catalogue. However, since App Thinning is enabled by default, the Watch Series 3 doesn't have the image in it's binary and therefor the following line will crash the app:
let image = UIImage(named: "Complication/Graphic Corner")!
We discovered this when we found thousands of crash reports in Xcode:
- Open Xcode
- Window -> Organizer
- Select tab "Crashes"
- Select an App Store version
Мы нашли отчет о сбоях для каждого из 2 поддерживаемых нами графических усложнений, все они были связаны с watchOS 5 или 6 и Watch Series 2 или 3, например:
Решение
Встроить загрузку графического ресурса в оператор IF и вернуть nil
как шаблон, поэтому он не выйдет из строя на устройствах без актива.
Приведенный выше пример от Золтана будет:
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()
который используется во всех трех обязательных функциях делегата:
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
}
}
Ответ @zoltan-vinkler для Apple Watch 7
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
switch complication.family {
case .graphicCircular:
let image = UIImage(named: "Complication/Graphic Circular")!
let template = CLKComplicationTemplateGraphicCircularImage(imageProvider: CLKFullColorImageProvider(fullColorImage: image))
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
default:
handler(nil)
}
}
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
switch complication.family {
case .graphicCircular:
let image = UIImage(named: "Complication/Graphic Circular")!
let template = CLKComplicationTemplateGraphicCircularImage(imageProvider: CLKFullColorImageProvider(fullColorImage: image))
let timelineEntry = CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template)
handler(timelineEntry)
default:
handler(nil)
}
}