Search code examples
iosswiftframeanimated-gifanimatedimagedrawable

How to combine a Gif Image into UIImageView with overlaying UIImageView in swift?


A gif image is loaded into a UIImageView (by using this extension) and another UIImageView is overlaid on it. Everything works fine but the problem is when I going for combine both via below code, it shows a still image (.jpg). I wanna combine both and after combine it should be a animated image (.gif) too.

let bottomImage = gifPlayer.image
let topImage = UIImage

let size = CGSize(width: (bottomImage?.size.width)!, height: (bottomImage?.size.height)!)
        
UIGraphicsBeginImageContext(size)
        
let areaSize = CGRect(x: 0, y: 0, width: size.width, height: size.height)
bottomImage!.draw(in: areaSize)

topImage!.draw(in: areaSize, blendMode: .normal, alpha: 0.8)
let newImage = UIGraphicsGetImageFromCurrentImageContext()

UIGraphicsEndImageContext()

Click here to know more about this problem please.


Solution

  • When using an animated GIF in a UIImageView, it becomes an array of UIImage.

    We can set that array with (for example):

    imageView.animationImages = arrayOfImages
    imageView.animationDuration = 1.0
    

    or, we can set the .image property to an animatedImage -- that's how the GIF-Swift code you are using works:

    if let img = UIImage.gifImageWithName("funny") {
        bottomImageView.image = img
    }
    

    in that case, the image also contains the duration:

    img.images?.duration
    

    So, to generate a new animated GIF with the border/overlay image, you need to get that array of images and generate each "frame" with the border added to it.

    Here's a quick example...

    This assumes:

    • you are using GIF-Swift
    • you have added bottomImageView and topImageView in Storyboard
    • you have a GIF in the bundle named "funny.gif" (edit the code if yours is different)
    • you have a "border.png" in assets (again, edit the code as needed)

    and you have a button to connect to the @IBAction:

    import UIKit
    import ImageIO
    import UniformTypeIdentifiers
    
    class animImageViewController: UIViewController {
    
        @IBOutlet var bottomImageView: UIImageView!
        @IBOutlet var topImageView: UIImageView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let img = UIImage.gifImageWithName("funny") {
                bottomImageView.image = img
            }
            
            if let img = UIImage(named: "border") {
                topImageView.image = img
            }
            
        }
        
        @IBAction func saveButtonTapped(_ sender: Any) {
            generateNewGif(from: bottomImageView, with: topImageView)
        }
    
        func generateNewGif(from animatedImageView: UIImageView, with overlayImageView: UIImageView) {
            
            var images: [UIImage]!
            var delayTime: Double!
            
            guard let overlayImage = overlayImageView.image else {
                print("Could not get top / overlay image!")
                return
            }
            
            if let imgs = animatedImageView.image?.images {
                // the image view is using .image = animatedImage
                // unwrap the duration
                if let dur = animatedImageView.image?.duration {
                    images = imgs
                    delayTime = dur / Double(images.count)
                } else {
                    print("Image view is using an animatedImage, but could not get the duration!" )
                    return
                }
            } else if let imgs = animatedImageView.animationImages {
                // the image view is using .animationImages
                images = imgs
                delayTime = animatedImageView.animationDuration / Double(images.count)
            } else {
                print("Could not get images array!")
                return
            }
            
            // we now have a valid [UIImage] array, and
            //  a valid inter-frame duration, and
            //  a valid "overlay" UIImage
        
            // generate unique file name
            let destinationFilename = String(NSUUID().uuidString + ".gif")
            
            // create empty file in temp folder to hold gif
            let destinationURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(destinationFilename)
            
            // metadata for gif file to describe it as an animated gif
            let fileDictionary = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFLoopCount : 0]]
            
            // create the file and set the file properties
            guard let animatedGifFile = CGImageDestinationCreateWithURL(destinationURL as CFURL, UTType.gif.identifier as CFString, images.count, nil) else {
                print("error creating file")
                return
            }
            
            CGImageDestinationSetProperties(animatedGifFile, fileDictionary as CFDictionary)
            
            let frameDictionary = [kCGImagePropertyGIFDictionary : [kCGImagePropertyGIFDelayTime: delayTime]]
            
            // use original size of gif
            let sz: CGSize = images[0].size
            
            let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(size: sz)
            
            // loop through the images
            //  drawing the top/border image on top of each "frame" image with 80% alpha
            //  then writing the combined image to the gif file
            images.forEach { img in
                
                let combinedImage = renderer.image { ctx in
                    img.draw(at: .zero)
                    overlayImage.draw(in: CGRect(origin: .zero, size: sz), blendMode: .normal, alpha: 0.8)
                }
                
                guard let cgFrame = combinedImage.cgImage else {
                    print("error creating cgImage")
                    return
                }
                
                // add the combined image to the new animated gif
                CGImageDestinationAddImage(animatedGifFile, cgFrame, frameDictionary as CFDictionary)
                
            }
            
            // done writing
            CGImageDestinationFinalize(animatedGifFile)
            
            print("New GIF created at:")
            print(destinationURL)
            print()
            
            // do something with the newly created file...
            //  maybe move it to documents folder, or
            //  upload it somewhere, or
            //  save to photos library, etc
        }
        
    }
    

    Notes: