通过iOS创建和导出动画gif吗?


74

我在iOS应用程序中有一系列用户自定义图像,这些图像以简单的逐帧翻转书样式进行动画处理。

我的问题是:有没有办法允许用户将其动画导出为动画gif?理想情况下,我希望使他们能够通过电子邮件发送电子邮件,社交共享(T / FB)或(最坏的情况。)将动画gif保存到他们的文档文件夹中,以便通过iTunes进行检索。

我知道如何将.png保存到图片库,并且找到了一种将动画录制为QT文件的方法(http://www.cimgf.com/2009/02/03/record-your-core-animation -animation /),但是我还没有找到一种方法来踢出普通的老式gif动画。我在核心动画或其他地方缺少什么吗?有没有人可以推荐的方法,框架或资源?很抱歉,这个问题过于笼统-努力寻找起点。

Answers:


161

您可以使用Image I / O框架(iOS SDK的一部分)创建动画GIF。您还将需要包括MobileCoreServices定义GIF类型常量的框架。您需要将这些框架添加到目标中,并将它们的标头导入要创建动画GIF的文件中,如下所示:

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

通过示例最容易解释。我将向您展示我在iPhone 5上制作此GIF的代码:

显示的代码创建的GIF动画

首先,这是一个帮助函数,该函数采用大小和角度,并以该角度返回UIImage红色磁盘的a :

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;
}

现在我们可以创建GIF。首先,我们将为帧数定义一个常量,因为以后需要两次:

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

我们需要一个属性字典来指定动画应重复的次数:

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

我们将需要另一个属性字典,该字典将附加到每个帧,指定该帧应显示多长时间:

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

我们还将在文档目录中为GIF创建URL:

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

现在,我们可以创建一个CGImageDestination将GIF写入指定URL的:

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

我发现传递fileProperties作为的最后一个参数CGImageDestinationCreateWithURL没有工作。您必须使用CGImageDestinationSetProperties

现在我们可以创建和编写框架:

    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);
        }
    }

请注意,我们将帧属性字典与每个帧图像一起传递。

精确添加指定数量的帧后,我们最终确定目标并释放它:

    if (!CGImageDestinationFinalize(destination)) {
        NSLog(@"failed to finalize image destination");
    }
    CFRelease(destination);

    NSLog(@"url=%@", fileURL);
}

如果您在模拟器上运行此代码,则可以从调试控制台复制URL,然后将其粘贴到浏览器中以查看图像。如果在设备上运行它,则可以使用Xcode Organizer窗口从设备下载应用程序沙箱并查看图像。或者您可以使用类似iExplorer来直接浏览设备的文件系统。(这不需要越狱。)

我在运行iOS 6.1的iPhone 5上进行了测试,但是我认为代码应该可以追溯到iOS 4.0之前。

为了方便复制,我将所有代码都放在了要点中。


1
罗布,今天我的技巧工作了,但是有很大的局限性。系统将动画序列中所有图像的数据保留在内存中,直到完成finalize调用为止。我正在创建一些非常大的GIF序列,并且以30 FPS的速度很快就可以耗尽内存并崩溃。有什么办法可以将帧缓冲到磁盘吗?我正在使用OpenGL创建CGImages,并使用数据提供程序。有一种数据提供程序可以从文件中读取其内容。我想知道是否会轮流读取每个帧并在最终确定GIF时将其释放。
Duncan C

3
快速的搜索和检查使我认为长颈鹿可能会做您想要的。
rob mayoff

1
@robmayoff Rob,您好,我已经使用您的代码一段时间了,但是我发现了一个奇怪的错误,如果输入序列中有彼此相邻的相同帧,我总是在CGImageDestinationFinalize(destination)处获得EXE_BAD_ACCESS。知道为什么会这样吗?
2013年

1
@Pascal这是一个系统错误,序列末尾不能有相同的帧,即,如果您有n个帧,则第n个和第n-1个帧不能相同。我最终将序列的尾部修剪为相同的帧。(而且我使用一种非常幼稚的方法来检测相同的帧,文件大小以及EXIF标签中的时间戳,对我来说效果很好...)
XY

1
@kevin检查此问题及其评论。我没有进行测试,但是我没有理由怀疑这些信息。
rob mayoff 2014年

1

对于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)")
            }
        }
    }
}

我已经从上面的答案将其转换。希望对您有所帮助。

可作为要点

欢迎进行编辑。


一个有趣的替代方法是在Arraywhere的扩展中实现此方法Element == UIImage
Nicolas Miari

好主意@NicolasMiari
Nikhil Manapure

0

如果您正在寻找Swift 3解决方案,则可以查看https://github.com/onmyway133/GifMagic。它具有EncoderDecodergif文件的汇编和反汇编。

基本上,你应该使用Image IO框架具有这些功能CGImageDestinationCreateWithURLCGImageDestinationSetPropertiesCGImageDestinationAddImageCGImageDestinationFinalize

同样使用Swift https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/WorkingWithCocoaDataTypes.html

从带注释的API返回的Core Foundation对象在Swift中会自动进行内存管理-您无需自己调用CFRetain,CFRelease或CFAutorelease函数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.