Search code examples
iosswiftimage-processinggifanimated-gif

How to Adjust Frame Delay for Each Image in Swift when Generating a GIF?


Currently I’m working on a project to create a GIF from some images. I used a method (generateGifFromImages) from GitHub that takes a CGImage structure, which allows you to set the frame delay for each image. But even with the frame delay set to 2 seconds per frame, the resulting GIFs seem to play much faster, typically around 0.5 seconds per frame.

Below method I used to convert images to gif.

public func generateGifFromImages(imagesArray: [CGImage], repeatCount: Int = 0, frameDelay: TimeInterval, targetSize: CGSize, destinationURL: URL, progressHandler: @escaping (Float) -> (), callback: @escaping (_ data: Data?, _ error: NSError?) -> ()) {
    
    DispatchQueue.global(qos: .background).async {
        if let imageDestination = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypeGIF, imagesArray.count, nil) {
            let gifProperties = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: repeatCount]]
            
            // Calculate total progress steps
            let totalSteps = imagesArray.count
            var currentStep = 0
            
            for image in imagesArray {
                // Resize image while maintaining aspect ratio
                guard let resizedImage = resize(image: image, targetSize: targetSize) else {
                    callback(nil, self.errorFromString("Couldn't resize image"))
                    return
                }
                
                let frameProperties: [String: Any] = [kCGImagePropertyGIFDelayTime as String: frameDelay]
                
                // Add the resized image to the GIF
                CGImageDestinationAddImage(imageDestination, resizedImage, frameProperties as CFDictionary )
                
                // Update progress
                currentStep += 1
                let progress = Float(currentStep) / Float(totalSteps)
                progressHandler(progress)
            }
            
            CGImageDestinationSetProperties(imageDestination, gifProperties as CFDictionary)
            
            if CGImageDestinationFinalize(imageDestination) {
                do {
                    let data = try Data(contentsOf: destinationURL)
                    callback(data, nil)
                } catch {
                    callback(nil, error as NSError)
                }
            } else {
                callback(nil, self.errorFromString("Couldn't create the final image"))
            }
        }
    }
}

I tried setting the frame delay to 2 seconds for each image using the kCGImagePropertyGIFDelayTime property, but the resulting GIF plays very fast. I have six images in the array, and I want at least a 1-second delay when each frame is played. Can someone help me please how to adjust the frame delay for each image so that the resulting GIF plays in slow motion, showing each frame for at least 1 second? Thank you in advance for your support!


Solution

  • The problem is the following line.

    let frameProperties: [String: Any] = [kCGImagePropertyGIFDelayTime as String: frameDelay]
    

    , which is [String : Double].

    You are supposed to set it as [String : [String : Double]]. So it should be

    let frameProperties = [(kCGImagePropertyGIFDictionary as String): [(kCGImagePropertyGIFDelayTime as String): frameDelay]]
    

    .