Create and export animated GIFs via iOS?

I have a series of user-configured images in an iOS app that are animated in a simple, frame-by-frame flip book style.

My question is: is there a way to allow users to export their animation as an animated GIF? Ideally, I would like them to be able to save an animated GIF file to a folder with documents for searching through iTunes via email, on social networks (T / FB) or (in the worst case ...).

I know how to save .png in the photo library, and I found a way to record the animation as a QT file ( http://www.cimgf.com/2009/02/03/record-your-core-animation -animation / ), but I didn’t find a way to throw out a plain old animated GIF. Am I missing something in Core Animation or somewhere else? Are there any approaches, frameworks or resources that anyone can recommend? Sorry if the question is too general - try to find a starting point.

+70
ios objective-c swift core-graphics animated-gif
Feb 16 '13 at 21:09
source share
3 answers

You can create an animated GIF using an image input / output environment (which is part of the iOS SDK). You will also want to include a MobileCoreServices structure that defines a constant of type GIF. You need to add these frames to your target and import their headers into the file in which you want to create an animated GIF, for example:

 #import <ImageIO/ImageIO.h> #import <MobileCoreServices/MobileCoreServices.h> 

The easiest way to explain with an example. I will show you the code that I used to create this GIF on my iPhone 5:

animated GIF created by the code shown

Firstly, here is a helper function that takes the size and angle and returns the UIImage red disk at that angle:

 static UIImage *frameImage(CGSize size, CGFloat radians) { UIGraphicsBeginImageContextWithOptions(size, YES, 1); { [[UIColor whiteColor] setFill]; UIRectFill(CGRectInfinite); CGContextRef gc = UIGraphicsGetCurrentContext(); CGContextTranslateCTM(gc, size.width / 2, size.height / 2); CGContextRotateCTM(gc, radians); CGContextTranslateCTM(gc, size.width / 4, 0); [[UIColor redColor] setFill]; CGFloat w = size.width / 10; CGContextFillEllipseInRect(gc, CGRectMake(-w / 2, -w / 2, w, w)); } UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image; } 

Now we can create a GIF. First we define a constant for the number of frames, because we need it twice later:

 static void makeAnimatedGif(void) { static NSUInteger const kFrameCount = 16; 

We will need a property dictionary to indicate the number of repetitions of the animation:

  NSDictionary *fileProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever } }; 

And we need one more property dictionary, which we will attach to each frame, indicating how long this frame should be displayed:

  NSDictionary *frameProperties = @{ (__bridge id)kCGImagePropertyGIFDictionary: @{ (__bridge id)kCGImagePropertyGIFDelayTime: @0.02f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data } }; 

We will also create a URL for GIF in the directory of our documents:

  NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"]; 

Now we can create a CGImageDestination that writes the GIF to the specified URL:

  CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, kFrameCount, NULL); CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties); 

I found that passing fileProperties as the last argument to CGImageDestinationCreateWithURL does not work. You must use CGImageDestinationSetProperties .

Now we can create and write our frames:

  for (NSUInteger i = 0; i < kFrameCount; i++) { @autoreleasepool { UIImage *image = frameImage(CGSizeMake(300, 300), M_PI * 2 * i / kFrameCount); CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties); } } 

Note that we pass the frame properties dictionary along with each frame image.

After we have added the exact specified number of frames, we complete the assignment and release it:

  if (!CGImageDestinationFinalize(destination)) { NSLog(@"failed to finalize image destination"); } CFRelease(destination); NSLog(@"url=%@", fileURL); } 

If you run this on a simulator, you can copy the URL from the debug console and paste it into your browser to see the image. If you run it on the device, you can use the Xcode Organizer window to download application sandboxes from the device and view the image. Or you can use an application such as iExplorer , which allows you to directly view the file system of your device. (This does not require jailbreak.)

I tested this on my iPhone 5 running iOS 6.1, but I believe the code should work in iOS 4.0 as well.

I put all the code in that sense for easy copying.

+160
Feb 16 '13 at 23:01
source share

If you are looking for a Swift 3 solution, you can take a look at https://github.com/onmyway133/GifMagic . It has an Encoder and Decoder that collects and disassembles a gif file.

Basically, you should use the Image IO structure with these functions CGImageDestinationCreateWithURL , CGImageDestinationSetProperties , CGImageDestinationAddImage , CGImageDestinationFinalize

Also with Swift https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

Core Foundation objects returned from annotated APIs are automatically memory-managed in Swift — you don’t need to run CFRetain, CFRelease, or CFAutorelease functions yourself.

0
Aug 01 '17 at 9:02 on
source share

For Swift 3

 import Foundation import UIKit import ImageIO import MobileCoreServices extension UIImage { static func animatedGif(from images: [UIImage]) { let fileProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [kCGImagePropertyGIFLoopCount as String: 0]] as CFDictionary let frameProperties: CFDictionary = [kCGImagePropertyGIFDictionary as String: [(kCGImagePropertyGIFDelayTime as String): 1.0]] as CFDictionary let documentsDirectoryURL: URL? = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let fileURL: URL? = documentsDirectoryURL?.appendingPathComponent("animated.gif") if let url = fileURL as CFURL? { if let destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, images.count, nil) { CGImageDestinationSetProperties(destination, fileProperties) for image in images { if let cgImage = image.cgImage { CGImageDestinationAddImage(destination, cgImage, frameProperties) } } if !CGImageDestinationFinalize(destination) { print("Failed to finalize the image destination") } print("Url = \(fileURL)") } } } } 

I converted it from the above answer . Hope this helps.

Available as gist .

Changes are welcome.

0
Sep 07 '17 at 12:05
source share



All Articles