UIImage+PYJAnimatedGIF.h:
#import <UIKit/UIKit.h> @interface UIImage (PYJAnimatedGIF) + (UIImage * _Nullable)animatedImageWithAnimatedGIFData:(NSData * _Nonnull)theData; + (UIImage * _Nullable)animatedImageWithAnimatedGIFURL:(NSURL * _Nonnull)theURL; + (UIImage * _Nullable)imageGIFWithName:(NSString * _Nonnull)theName; + (UIImage * _Nullable)imageGIFWithURL:(NSURL * _Nonnull)theURL; + (UIImage * _Nullable)imageGIFWithData:(NSData * _Nonnull)theData; @end
UIImage+PYJAnimatedGIF.m:
#import "UIImage+PYJAnimatedGIF.h" #import <ImageIO/ImageIO.h> #if __has_feature(objc_arc) #define toCF (__bridge CFTypeRef) #define fromCF (__bridge id) #else #define toCF (CFTypeRef) #define fromCF (id) #endif @implementation UIImage (PYJAnimatedGIF) static int delayCentisecondsForImageAtIndex(CGImageSourceRef const source, size_t const i) { int delayCentiseconds = 1; CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL); if (properties) { CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary); if (gifProperties) { NSNumber *number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime); if (number == NULL || [number doubleValue] == 0) { number = fromCF CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime); } if ([number doubleValue] > 0) { delayCentiseconds = (int)lrint([number doubleValue] * 100); } } CFRelease(properties); } return delayCentiseconds; } static void createImagesAndDelays(CGImageSourceRef source, size_t count, CGImageRef imagesOut[count], int delayCentisecondsOut[count]) { for (size_t i = 0; i < count; ++i) { imagesOut[i] = CGImageSourceCreateImageAtIndex(source, i, NULL); delayCentisecondsOut[i] = delayCentisecondsForImageAtIndex(source, i); } } static int sum(size_t const count, int const *const values) { int theSum = 0; for (size_t i = 0; i < count; ++i) { theSum += values[i]; } return theSum; } static int pairGCD(int a, int b) { if (a < b) return pairGCD(b, a); while (true) { int const r = a % b; if (r == 0) return b; a = b; b = r; } } static int vectorGCD(size_t const count, int const *const values) { int gcd = values[0]; for (size_t i = 1; i < count; ++i) { gcd = pairGCD(values[i], gcd); } return gcd; } static NSArray *frameArray(size_t const count, CGImageRef const images[count], int const delayCentiseconds[count], int const totalDurationCentiseconds) { int const gcd = vectorGCD(count, delayCentiseconds); size_t const frameCount = totalDurationCentiseconds / gcd; UIImage *frames[frameCount]; for (size_t i = 0, f = 0; i < count; ++i) { UIImage *const frame = [UIImage imageWithCGImage:images[i]]; for (size_t j = delayCentiseconds[i] / gcd; j > 0; --j) { frames[f++] = frame; } } return [NSArray arrayWithObjects:frames count:frameCount]; } static void releaseImages(size_t const count, CGImageRef const images[count]) { for (size_t i = 0; i < count; ++i) { CGImageRelease(images[i]); } } static UIImage *animatedImageWithAnimatedGIFImageSource(CGImageSourceRef const source) { size_t const count = CGImageSourceGetCount(source); CGImageRef images[count]; int delayCentiseconds[count]; // in centiseconds createImagesAndDelays(source, count, images, delayCentiseconds); int const totalDurationCentiseconds = sum(count, delayCentiseconds); NSArray *const frames = frameArray(count, images, delayCentiseconds, totalDurationCentiseconds); UIImage *const animation = [UIImage animatedImageWithImages:frames duration:(NSTimeInterval)totalDurationCentiseconds / 100.0]; releaseImages(count, images); return animation; } static UIImage *animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceRef CF_RELEASES_ARGUMENT source) { if (source) { UIImage *const image = animatedImageWithAnimatedGIFImageSource(source); CFRelease(source); return image; } else { return nil; } } + (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)data { return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF data, NULL)); } + (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)url { return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL)); } + (UIImage *)imageGIFWithName:(NSString *)theName { NSURL *url = [[NSBundle mainBundle] URLForResource:theName withExtension:@"gif"]; return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL)); } + (UIImage *)imageGIFWithURL:(NSURL *)theURL { return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF theURL, NULL)); } + (UIImage *)imageGIFWithData:(NSData *)theData { return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF theData, NULL)); } @end