Search code examples
iosimage-processinggifswift3animated-gif

Generate and export an animated gif via Swift 3.0?


I've learned that the Image IO Framework has changed syntactically since iOS 9 according to the documentation, however I have done my research and the following code seems to be correct. I have to procedures listed below; one procedure takes images and writes those images to the application's document folder as a gif. I can confirm this works as I can view the actually gif file if I go to the app's documents folder using iTunes. Despite this, in the second procedure where I attempt to read from that same file, an error is throw which states the file at that path does not exist. I have posted the code below.

class GifManager {
private func getDocumentsDirectory() -> URL?  {
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
}

public func generateGif(photos: [UIImage], filename: String) -> Bool {
    if let docsDirectory = getDocumentsDirectory() {
        let url = docsDirectory.appendingPathComponent(filename)
        let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
        let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.125]]
        if let destination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeGIF, photos.count, nil) {
            CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
            for photo in photos {
                CGImageDestinationAddImage(destination, photo.cgImage!, gifProperties as CFDictionary?)
            }
            return CGImageDestinationFinalize(destination)
        }
    }
    return false
}

public func saveGifToCameraRoll(filename: String) {
    if let docsDirectory = getDocumentsDirectory() {
        let fileUrl: URL = docsDirectory.appendingPathComponent(filename)
        do {
            let data = try Data(contentsOf: fileUrl)
            if let _ = UIImage(data: data) {
                PHPhotoLibrary.shared().performChanges({
                    PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: fileUrl)
                    }, completionHandler: {completed, error in
                        if error != nil {
                            print("error")
                        } else if completed {
                            print("completed")
                        } else {
                            print("not completed")
                        }
                })
            }

        } catch let error {
            print(error)
        }
    }
}

Solution

  • Swift 3.1, 4 and 5

    For those wanting an updated version of the GIF generation function, I have included it here.

    This function requires the ImageIO and MobileCoreServices import statements.

    import ImageIO
    import MobileCoreServices
    

    Here is the function.

    func generateGif(photos: [UIImage], filename: String) -> Bool {
        let documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        let path = documentsDirectoryPath.appending(filename)
        let fileProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]]
        let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFDelayTime as String: 0.125]]
        let cfURL = URL(fileURLWithPath: path) as CFURL
        if let destination = CGImageDestinationCreateWithURL(cfURL, kUTTypeGIF, photos.count, nil) {
                CGImageDestinationSetProperties(destination, fileProperties as CFDictionary?)
                for photo in photos {
                    CGImageDestinationAddImage(destination, photo.cgImage!, gifProperties as CFDictionary?)
                }
                return CGImageDestinationFinalize(destination)
            }
        return false
    }
    

    EDIT:

    It has a Bool so you know you can safely use the file it creates.

    if generateGif(arrayOfImages, "/myGIFfile.gif") {
        // do something with gif
    } else {
        // failed to create and close the gif file
    }