Search code examples
swiftxcodeswift5apple-watchwatchos

How to provide Apple Watch Complication Asset for 45mm?


When adding assets for the Graphic Circular Complication there is no option to add an asset for the 45mm version, thus the image does not fill the available space.

enter image description here

Result (The image does not fill the space as it is too small):

enter image description here

I have read that I need to use PDF assets for the 40/42mm but my image is a raster image and thus I can't create it as a PDF. I want to scale the image myself and add it as an asset but there is no option to drop it.

What should I do?


Solution

  • The issue is that the size of the image in the asset catalog is smaller than it really should be according to the Apple Human Interface Guidelines. Thus this causes the images not to be filled. As there's no option to drop the 45mm version you need to calculate and resize the image yourself.

    This article is the solution!

    http://www.glimsoft.com/02/18/watchos-complications/?utm_campaign=iOS%2BDev%2BWeekly&utm_medium=web&utm_source=iOS%2BDev%2BWeekly%2BIssue%2B547

    ComplicationController+Ext.swift

    extension ComplicationController {
        enum ComplicationImageType {
            case graphicCircularImage
        }
        
        struct ComplicationImageSizeCollection {
            var size38mm: CGFloat = 0
            let size40mm: CGFloat
            let size41mm: CGFloat
            let size44mm: CGFloat
            let size45mm: CGFloat
            
            // The following sizes are taken directly from HIG: https://developer.apple.com/design/human-interface-guidelines/watchos/overview/complications/
            static let graphicCircularImageSizes = ComplicationImageSizeCollection(size40mm: 42, size41mm: 44.5, size44mm: 47, size45mm: 50)
            
            func sizeForCurrentWatchModel() -> CGFloat {
                let screenHeight = WKInterfaceDevice.current().screenBounds.size.height
                if screenHeight >= 242 {
                    // It's the 45mm version..
                    return self.size45mm
                }
                else if screenHeight >= 224 {
                    // It's the 44mm version..
                    return self.size44mm
                }
                else if screenHeight >= 215 {
                    // It's the 41mm version..
                    return self.size41mm
                }
                else if screenHeight >= 197 {
                    return self.size40mm
                }
                else if screenHeight >= 170 {
                    return self.size38mm
                }
                return self.size40mm    // Fallback, just in case.
            }
            
            static func sizes(for type: ComplicationImageType) -> ComplicationImageSizeCollection {
                switch type {
                case .graphicCircularImage: return Self.graphicCircularImageSizes
                }
            }
            
            static func getImage(for type: ComplicationImageType) -> UIImage {
                let complicationImageSizes = ComplicationImageSizeCollection.sizes(for: .graphicCircularImage)
                let width = complicationImageSizes.sizeForCurrentWatchModel()
                let size = CGSize(width: width, height: width)
                
                var filename: String!
                
                switch type {
                case .graphicCircularImage: filename = "gedenken_graphic_circular_pdf"
                }
                
                return renderPDFToImage(named: filename, outputSize: size)
            }
            
            static private func renderPDFToImage(named filename: String, outputSize size: CGSize) -> UIImage {
                
                // Create a URL for the PDF file
                let resourceName = filename.replacingOccurrences(of: ".pdf", with: "")
                let path = Bundle.main.path(forResource: resourceName, ofType: "pdf")!
                let url = URL(fileURLWithPath: path)
                
                guard let document = CGPDFDocument(url as CFURL),
                      let page = document.page(at: 1) else {
                    fatalError("We couldn't find the document or the page")
                }
                
                let originalPageRect = page.getBoxRect(.mediaBox)
                
                // With the multiplier, we bring the pdf from its original size to the desired output size.
                let multiplier = size.width / originalPageRect.width
                
                UIGraphicsBeginImageContextWithOptions(size, false, 0)
                let context = UIGraphicsGetCurrentContext()!
                    
                // Translate the context
                context.translateBy(x: 0, y: (originalPageRect.size.height * multiplier))
                
                // Flip the context vertically because the Core Graphics coordinate system starts from the bottom.
                context.scaleBy(x: multiplier * 1.0, y: -1.0 * multiplier)
                
                // Draw the PDF page
                context.drawPDFPage(page)
                
                let image = UIGraphicsGetImageFromCurrentImageContext()!
                UIGraphicsEndImageContext()
                
                return image
            }
        }
    }
    

    ComplicationController.swift

    func createGraphicCircularTemplate() -> CLKComplicationTemplate {
        let template = CLKComplicationTemplateGraphicCircularImage()
        let imageLogoProvider = CLKFullColorImageProvider()
        
        imageLogoProvider.image = ComplicationImageSizeCollection.getImage(for: .graphicCircularImage)
        template.imageProvider = imageLogoProvider
        
        return template
    }