GnohiSiaM
7/11/2017 - 3:37 PM

UIImage.gif

UIImage.gif(name: "imageName") imageView.loadGif(name: "imageName")

//
//  https://github.com/bahlo/SwiftGif
//
//
//  Created by Jim Du on 11/07/2017.
//  Copyright © 2017 Jim Du. All rights reserved.
//

import UIKit
import ImageIO

extension UIImage {

    public class func gif(data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            print("UIImageGif: Source for the image does not exist")
            return nil
        }

        return UIImage.animatedImageWithSource(source)
    }

    public class func gif(url: String) -> UIImage? {
        guard let bundleURL = URL(string: url) else {
            print("UIImageGif: This image named \"\(url)\" does not exist")
            return nil
        }

        guard let imageData = try? Data(contentsOf: bundleURL) else {
            print("UIImageGif: Cannot turn image named \"\(url)\" into NSData")
            return nil
        }

        return gif(data: imageData)
    }

    public class func gif(name: String) -> UIImage? {
        guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
            print("UIImageGif: This image named \"\(name)\" does not exist")
            return nil
        }

        guard let imageData = try? Data(contentsOf: bundleURL) else {
            print("UIImageGif: Cannot turn image named \"\(name)\" into NSData")
            return nil
        }

        return gif(data: imageData)
    }

    internal class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
        var delay = 0.1

        // Get dictionaries
        let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
        let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)

        guard CFDictionaryGetValueIfPresent(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque(), gifPropertiesPointer) else {
            return delay
        }

        let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)

        // Get delay time
        var delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()), to: AnyObject.self)
        if delayObject.doubleValue == 0 {
            delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
        }

        delay = delayObject as? Double ?? 0
        return max(delay, 0.1) // Make sure they're not too fast
    }

    internal class func gcdForPair(_ left: Int?, _ right: Int?) -> Int {
        // Check if one of them is nil
        guard var a = right, var b = left else {
            return right ?? left ?? 0
        }

        // Get greatest common divisor
        var rest: Int
        while true {
            rest = a % b

            if rest == 0 {
                return b
            }

            a = b
            b = rest
        }
    }

    internal class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
        let count = CGImageSourceGetCount(source)
        var images = [CGImage]()
        var delays = [Int]()

        // Fill arrays
        for i in 0..<count {
            // Add image
            if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
                images.append(image)
            }

            // At it's delay in cs
            let delaySeconds = UIImage.delayForImageAtIndex(i, source: source)
            delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
        }

        // Calculate full duration
        let duration = delays.reduce(0, { $0 + $1 })

        // Get frames
        let gcd = delays.isEmpty ? 1 : delays.reduce(delays[0], { UIImage.gcdForPair($1, $0) })

        var frames = [UIImage]()
        var frame: UIImage
        for i in 0..<count {
            frame = UIImage(cgImage: images[Int(i)])
            for _ in 0..<Int(delays[i] / gcd) {
                frames.append(frame)
            }
        }

        return UIImage.animatedImage(with: frames, duration: Double(duration) / 1000.0)
    }
    
}

extension UIImageView {

    public func loadGif(name: String) {
        DispatchQueue.global().async {
            let image = UIImage.gif(name: name)
            DispatchQueue.main.async {
                self.image = image
            }
        }
    }
    
}