WatchOS3 Complication that launches App

杀马特。学长 韩版系。学妹 提交于 2020-12-24 14:27:53

问题


I would like to create a complication for watchOS 3 that will simply launch my App. I have used XCode to create the 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)
    }

}

and added images for the Circular, Modular and Utilitarian assets. But when I run the Watch App I cannot select my complications for a Watch face. What do I still need to do?

Thanks

Greg


回答1:


These code changes are required:

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)
    }
}

Plus you need to provide the images as assets in the extension.




回答2:


Apple Watch 4 new graphic complications looks like this:

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)
    }
}



回答3:


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:

  1. Open Xcode
  2. Window -> Organizer
  3. Select tab "Crashes"
  4. Select an App Store version

We found a crash report for each of the 2 Graphic complications that we support, all were with watchOS 5 or 6 and Watch Series 2 or 3, like:

Solution

Embed loading of the graphic asset within an IF statement and return nil as template, so it won't crash on devices without the asset.

The example above from Zoltan will be:

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)
        }

For efficient and maintainable code, we created a reusable function templateForComplication() that is used in all 3 mandatory delegate functions:

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
    }
}


来源:https://stackoverflow.com/questions/39708407/watchos3-complication-that-launches-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!