Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@toddheasley
Last active December 2, 2020 15:32
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save toddheasley/1d8fbe3621c935789015 to your computer and use it in GitHub Desktop.
Save toddheasley/1d8fbe3621c935789015 to your computer and use it in GitHub Desktop.
Animated GIF Support for UIImage
import UIKit
extension UIImage {
public enum GIFBehavior {
case unclamped, clamped(TimeInterval), webkit
fileprivate func duration(_ properties: Any?) -> TimeInterval? {
// Look for specified image duration; always prefer unclamped delay time
guard let properties: [String: Any] = (properties as? [String: Any])?["{GIF}"] as? [String: Any],
let duration: TimeInterval = properties["UnclampedDelayTime"] as? TimeInterval ?? properties["DelayTime"] as? TimeInterval else {
return nil
}
// Apply appropriate clamping behavior
switch self {
case .unclamped:
return max(duration, 0.0) // Respect natural delay time
case .clamped(let clamp):
return max(duration, max(clamp, 0.0)) // Clamp to custom delay time
case .webkit:
return max(duration, 0.1) // Mimic WebKit behavior
}
}
}
public static func animatedImage(data: Data, behavior: GIFBehavior = .webkit) -> UIImage? {
guard let source: CGImageSource = CGImageSourceCreateWithData(data as CFData, nil), CGImageSourceGetCount(source) > 1 else {
return Self(data: data) // Delegate ineligible image data to the designated data constructor
}
// Collect key frames and durations
let frames: [(image: CGImage, duration: TimeInterval)] = (0 ..< CGImageSourceGetCount(source)).compactMap { index in
guard let image: CGImage = CGImageSourceCreateImageAtIndex(source, index, nil),
let duration: TimeInterval = behavior.duration(CGImageSourceCopyPropertiesAtIndex(source, index, nil)) else {
return nil // Drop bad frames
}
return (image, duration)
}
// Convert key frames to animated image
var images: [UIImage] = []
var duration: TimeInterval = 0.0
for frame in frames {
let image = UIImage(cgImage: frame.image)
for _ in 0 ..< Int(frame.duration * 100.0) {
images.append(image) // Add fill frames
}
duration += frame.duration
}
return animatedImage(with: images, duration: round(duration * 10.0) / 10.0)
}
}
@dirkz
Copy link

dirkz commented May 14, 2018

What's the license of this? Could you please add one?

@toddheasley
Copy link
Author

toddheasley commented Apr 12, 2020

Available for Swift Package Manager, MIT license: github.com/toddheasley/gif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment