AnimatedGIFImageSerialization 编码教程:如何把 UIImage 序列导出为 GIF(duration 与 loopCount 详解)
【免费下载链接】AnimatedGIFImageSerializationComplete Animated GIF Support for iOS, with Functions, NSJSONSerialization-style Class, and (Optional) UIImage Swizzling项目地址: https://gitcode.com/gh_mirrors/an/AnimatedGIFImageSerialization
AnimatedGIFImageSerialization 是 iOS 开发中非常经典的动图处理库,它提供了两大核心能力:把 UIImage 序列导出为 GIF(编码)与把 GIF 解码为 UIImage,API 风格模仿系统自带的 NSJSONSerialization,上手成本极低。本篇文章是一份面向初学者的 GIF 编码教程,重点讲透导出 GIF 时最容易被忽视、也最影响效果的两个参数:duration(每帧时长)与loopCount(循环次数)。看完你就能用animatedGIFDataWithImage:一行代码把自己的多张 UIImage 打包成 GIF 动图。

为什么需要把 UIImage 序列导出为 GIF?
很多新手会踩同一个坑:UIImage原生并不支持保存成 GIF。在 iOS 13 之前,系统只默认帮你导出 PNG / JPEG 静态图,想把一组图片做成动图,就得自己折腾 ImageIO 和CGImageDestination,代码又长又容易出错。
AnimatedGIFImageSerialization 正是为了解决这个问题而生:
- 提供类似 NSJSONSerialization 的一键式 API,编码解码各一行搞定;
- 底层基于 ImageIO,自动处理帧延迟与循环次数的写入;
- 可选开启 UIImage 方法交换(Swizzling),让
[UIImage imageNamed:@"xxx.gif"]直接支持 GIF 解码。
所有公开 API 声明都集中在头文件 AnimatedGIFImageSerialization.h 中,核心实现则在 AnimatedGIFImageSerialization.m 里,源码清晰,很适合当作学习 ImageIO 编码的范例。
快速上手:animatedGIFDataWithImage 一键导出 GIF
编码入口非常简单,只需一行调用:
NSData *data = [AnimatedGIFImageSerialization animatedGIFDataWithImage:image duration:1.0 loopCount:0 error:nil];其中:
image:可以是由多帧组成的动态 UIImage(内部通过image.images取出每一帧),也可以是单张静态图;duration:整段动画的总时长(秒);loopCount:GIF 循环次数,0表示无限循环;error:可选的错误信息输出参数,失败时返回nil。
如果不传 duration 和 loopCount,还有更简短的版本animatedGIFDataWithImage:error:,内部默认按duration = 0、loopCount = 0处理。拿到NSData后,写入文件或上传服务器都随你。
duration 参数详解:如何控制 GIF 动画速度
动画快慢是 GIF 观感的关键,而它完全由duration决定。源码里的换算逻辑如下:
- 库会先取帧数:
frameCount = image.images.count; - 每帧时长 = 总时长 ÷ 帧数:
frameDuration = duration / frameCount; - 由于 GIF 格式的帧延迟只能以百分之一秒(厘秒)为单位存储,库会四舍五入:
lrint(frameDuration * 100),也就是精度约为 0.01 秒; - 最终把这个厘秒值写入每一帧的
kCGImagePropertyGIFDelayTime。
举个例子:你有 10 帧图片,想让每帧显示 0.1 秒,那么传duration = 1.0即可(1 秒 ÷ 10 帧 = 0.1 秒/帧)。
还有一个实用的小细节:当duration传0(或负数)时,库会自动回退使用image.duration / frameCount,也就是沿用 UIImage 自带的总时长信息。这意味着用[UIImage animatedImageWithImages:duration:]创建的动态图,直接交给编码方法也能得到合理的速度,无需手动算时长。
loopCount 参数详解:GIF 循环次数怎么设
循环次数是另一个高频问题。在 GIF 规范中,循环计数写入的是kCGImagePropertyGIFLoopCount,AnimatedGIFImageSerialization 通过CGImageDestinationSetProperties在文件头写入该值。它的取值含义如下:
| loopCount 取值 | 效果 |
|---|---|
0 | 无限循环(默认值,最常用) |
1 | 只播放一次,播完停住 |
N | 循环播放 N 次 |
所以如果你想要"播一次就停"的效果,传loopCount = 1;想要表情包那种无限转圈,就用默认的0。值得注意的是:传0恰恰是无限循环,很多新手误以为 0 表示不循环,结果做出"永远停不下来"的 GIF,搞清这个语义能帮你少踩一个坑。
完整编码示例:多张 UIImage 组装成 GIF
把编码教程落到实处,最常见的场景是把一组静态图拼成动图,完整代码如下:
// 1. 准备多帧图片 NSMutableArray<UIImage *> *frames = [NSMutableArray array]; for (int i = 0; i < 10; i++) { [frames addObject:[UIImage imageNamed:[NSString stringWithFormat:@"frame_%d", i]]]; } // 2. 组装成动态 UIImage,设置总时长 2 秒 UIImage *animatedImage = [UIImage animatedImageWithImages:frames duration:2.0]; // 3. 导出 GIF,每帧 0.2 秒,无限循环 NSData *gifData = [AnimatedGIFImageSerialization animatedGIFDataWithImage:animatedImage duration:2.0 loopCount:0 error:nil]; // 4. 写入沙盒或相册 [gifData writeToFile:somePath atomically:YES];整个流程就四步,不需要手动创建CGImageDestination,也不需要逐个帧写属性——这些脏活累活都封装在了UIImageAnimatedGIFRepresentation这个函数里。
解码也不难:imageNamed 直接加载 GIF
既然是"完整"的 GIF 解决方案,解码同样值得体验。默认情况下,库通过 Method Swizzling 增强了UIImage的imageNamed:、imageWithData:、initWithContentsOfFile:等方法,你只需要像加载普通图片一样写:
UIImageView *imageView = ...; imageView.image = [UIImage imageNamed:@"animated.gif"];GIF 就能自动解码成动画。它还会自动适配@2x、@3x以及不同屏幕高度(-568h、-667h、-736h)的资源命名,细节相当贴心。如果你不希望改动 UIImage 的默认行为,可以在构建环境定义宏ANIMATED_GIF_NO_UIIMAGE_INITIALIZER_SWIZZLING来关闭 Swizzling。
常见问题与避坑指南
围绕 duration 和 loopCount,这里再总结几个高频问题:
- 时长精度:GIF 帧延迟最小单位为 0.01 秒,过小的帧间隔会被四舍五入,别指望做 60fps 的细腻动画;
- duration 传 0 的含义:不是"零时长",而是"自动用 image.duration",语义要记牢;
- loopCount 传 0 的含义:不是"不循环",而是"无限循环";
- 错误处理:编码失败时返回
nil,并把错误信息写入error,错误域为AnimatedGIFImageErrorDomain(com.compuserve.gif.image.error),建议正式项目务必检查返回值; - iOS 13+ 的替代方案:作者已在 README 中声明本项目不再维护,苹果从 iOS 13 / macOS 10.15 起提供了原生接口
CGAnimateImageAtURLWithBlock,新项目可优先考虑系统方案;老项目或需要自定义帧率、循环次数时,这套库依然非常顺手。
获取源码与总结
想亲自跑一遍这个编码教程,可以直接克隆仓库:
git clone https://gitcode.com/gh_mirrors/an/AnimatedGIFImageSerialization仓库自带完整的 Xcode 工程示例(见Example/Animated GIF Example),里面有现成的演示 GIF 和 AppDelegate 样例,配合本教程上手更快。核心文件就两个:
- 头文件
AnimatedGIFImageSerialization/AnimatedGIFImageSerialization.h:查看全部 API; - 实现文件
AnimatedGIFImageSerialization/AnimatedGIFImageSerialization.m:duration 换算、loopCount 写入的完整逻辑都在这里。
总结一句话:把 UIImage 序列导出为 GIF,记住 duration 控制速度(总时长 ÷ 帧数 = 每帧时长)、loopCount 控制循环(0 = 无限循环),再配合animatedGIFDataWithImage:的一行式 API,你就能快速为自己的 App 加入动图导出能力了。如果对 GIF 的帧延迟、循环语义还有疑问,欢迎对照源码逐行阅读,相信会有更深的收获!
【免费下载链接】AnimatedGIFImageSerializationComplete Animated GIF Support for iOS, with Functions, NSJSONSerialization-style Class, and (Optional) UIImage Swizzling项目地址: https://gitcode.com/gh_mirrors/an/AnimatedGIFImageSerialization
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考