Skip to content

Instantly share code, notes, and snippets.

@kazuhiro4949
Last active July 29, 2018 01:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kazuhiro4949/85065375f5f8ea96444eeb32f4891f1e to your computer and use it in GitHub Desktop.
Save kazuhiro4949/85065375f5f8ea96444eeb32f4891f1e to your computer and use it in GitHub Desktop.
One shot gif animation with UIImageView.images
import UIKit
import ImageIO
// make one shot gif animation from Gif.
let gif = Gif(with: "GIF File Name (not in asset catalog)")!
let base: Double = 4000
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
// set start image before starting animation
imageView.image = gif.frames.first
// set UIImageView animation
imageView.animationImages = gif.frames
imageView.animationDuration = gif.delays.reduce(0, +) / base
imageView.animationRepeatCount = 1
// start animation
imageView.startAnimating()
// set end image after starting animation
imageView.image = gif.frames.last
/**********
Gif Class
***********/
// convert gif data to UIImages and delays (duration).
// Modification from https://github.com/swiftgif/SwiftGif/blob/master/SwiftGifCommon/UIImage+Gif.swift
class Gif {
let frames: [UIImage]
let delays: [Double]
init?(with fileName: String) {
guard let url = Bundle.main.url(forResource: fileName, withExtension: ".gif"),
let data = try? Data(contentsOf: url),
let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else {
return nil
}
let count = CGImageSourceGetCount(imageSource)
var _images = [CGImage]()
var _delays = [Int]()
(0..<count).forEach { (index) in
if let image = CGImageSourceCreateImageAtIndex(imageSource, index, nil) {
_images.append(image)
}
_delays.append(Int(Gif.delay(at: index, source: imageSource) * 1000))
}
let gcd = Gif.calcGcd(for: _delays)
var _frames = [UIImage]()
(0..<count).forEach { (index) in
let frame = UIImage(cgImage: _images[index])
let frameCount = _delays[index] / gcd
(0..<frameCount).forEach { _ in
_frames.append(frame)
}
}
self.frames = _frames
self.delays = _delays.map(Double.init)
}
static func calcGcd(for array: [Int]) -> Int {
guard !array.isEmpty else {
return 1
}
return array.reduce(array[0]) { (gcd, elem) in
return calcGcd(elem, gcd)
}
}
// https://github.com/raywenderlich/swift-algorithm-club/tree/master/GCD#greatest-common-divisor
static func calcGcd(_ a: Int, _ b: Int) -> Int {
let r = a % b
if r != 0 {
return calcGcd(b, r)
} else {
return b
}
}
static func delay(at index: Int, source: CGImageSource) -> Double {
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifPropertyPointer = CFDictionaryGetValue(
cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque())
let gifProperty = unsafeBitCast(gifPropertyPointer, to: CFDictionary.self)
let unclampedDelayTimeObjPointer = CFDictionaryGetValue(
gifProperty,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque())
var delayTime = unsafeBitCast(unclampedDelayTimeObjPointer, to: Double.self)
if delayTime == 0 {
let delayTimeObjPointer = CFDictionaryGetValue(gifProperty, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque())
delayTime = unsafeBitCast(delayTimeObjPointer, to: Double.self)
}
return max(delayTime, 0.1)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment