swiftuiimagewatchkitwkinterfacegroupwatchos-3

Save and Load Core Graphic UIImage Array on watchOS


I would like to be able to save a UIImage array created on the Apple Watch with watchOS and play this series of images as an animation as a group background. I can make the image array and play it but I cannot figure out how to store/save these images so I can retrieve/load them the next time I run the app so I don't have to build them every time the app runs.

Here is an example of how I am building the images with Core Graphics (Swift 3):

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController
{
    @IBOutlet var colourGroup: WKInterfaceGroup!

    override func awake(withContext context: AnyObject?)
    {
        super.awake(withContext: context)

    }

    override func willActivate()
    {
        var imageArray: [UIImage] = []
        for imageNumber in 1...250
        {
            let newImage: UIImage = drawImage(fade: CGFloat(imageNumber)/250.0)
            imageArray.append(newImage)
        }

        let animatedImages = UIImage.animatedImage(with:imageArray, duration: 10)

        colourGroup.setBackgroundImage(animatedImages)
        let imageRange: NSRange = NSRange(location: 0, length: 200)
        colourGroup.startAnimatingWithImages(in: imageRange, duration: 10, repeatCount: 0)

        super.willActivate()
    }

    func drawImage(fade: CGFloat) -> UIImage
    {
        let boxColour: UIColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: fade)

        let opaque: Bool = false
        let scale: CGFloat = 0.0

        let bounds: CGRect = WKInterfaceDevice.current().screenBounds

        let imageSize: CGSize = CGSize(width: bounds.width, height: 20.0)


        UIGraphicsBeginImageContextWithOptions(imageSize, opaque, scale)

        let radius: CGFloat = imageSize.height/2.0

        let rect: CGRect = CGRect(x: 0.0, y: 0.0, width: imageSize.width, height: imageSize.height)

        let selectorBox: UIBezierPath = UIBezierPath(roundedRect: rect, cornerRadius: radius)

        let boxLineWidth: Double = 0.0
        selectorBox.lineWidth = CGFloat(boxLineWidth)
        boxColour.setFill()
        selectorBox.fill()

        // return the image
        let result: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return result

    }

    override func didDeactivate()
    {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }

}

Basically I am looking for a way to save and load a [UIImage] in a manner that I can use UIImage.animatedImage(with:[UIImage], duration: TimeInterval) with the array

Is there a way to save the image array so I can load it next time I run the app rather than rebuild the images?

Thanks

Greg


Solution

  • NSKeyedArchiver and NSKeyedUnarchiver did the trick. Here is Swift code for XCode 8b4:

    override func willActivate()
    {
        var imageArray: [UIImage] = []
    
        let fileName: String = "TheRings"
        let fileManager = FileManager.default
        let url = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first! as NSURL
        let theURL: URL = url.appendingPathComponent(fileName)!
    
    
        if let rings = NSKeyedUnarchiver.unarchiveObject(withFile: theURL.path) as? [UIImage]
        {
            print("retrieving rings - found rings")
            imageArray = rings
        }
        else
        {
            print("retrieving rings - can't find rings, building new ones")
    
            for imageNumber in 1...250
            {
                let newImage: UIImage = drawImage(fade: CGFloat(imageNumber)/250.0)
                imageArray.append(newImage)
            }
             NSKeyedArchiver.archiveRootObject(imageArray, toFile: theURL.path)
        }
    
        let animatedImages = UIImage.animatedImage(with:imageArray, duration: 10)
    
        colourGroup.setBackgroundImage(animatedImages)
        let imageRange: NSRange = NSRange(location: 0, length: 200)
        colourGroup.startAnimatingWithImages(in: imageRange, duration: 10, repeatCount: 0)
    
        super.willActivate()
    }