前言
YYImage 是一个强大的iOS图像框架,是YYKit 的组件之一。具体用法可以参考Demo。
特性:
文件结构
- YYImage : UIImage的子类,遵守 YYAnimatedImage 协议,更高级的方式来显示 Image
- YYFrameImage : 同 YYImage,能够显示帧动画,仅支持png,jpeg 格式,可以配合 YYAnimatedImageView 来播放动画
- YYSpriteSheetImage : 同 YYFrameImage, 实现另一种动画形式,可以将一张图片自定义剪裁成多张图片来播放图片动画,同样可以配合YYAnimatedImageView 使用
- YYImageCoder : 图像的编码和解码功能类,
YYImageEncoder负责编码,YYImageDecoder负责解码,YYImageFrame负责管理帧图像信息,_YYImageDecoderFrame内部私有类是其子类- YYAnimatedImageView: UIImageView 子类,用于播放图像动画,定义了 YYAnimatedImage 协议
YYAnimatedImageView
运行Demo,进入Animated Image 。有五种播放的图片动画,前三个图像格式分别是GIF,WebP,APNG,后两个分别转化成 YYFrameImage,YYSpriteSheetImage ,全部都是通过YYAnimatedImageView 播放动画的,我们进入 YYAnimatedImageView 来分析
|
|
初始化方法只是一些简单的属性设置,默认_autoPlayAnimatedImage 为YES,自动开启动画,_runloopMode 为 NSRunLoopCommonModes,避免滑动时动画停止,因为以上的五种动画都是通过 CADisplayLink来实现的动画, 滑动时 mode 会切换到 UITrackingRunLoopMode 导致 _link 失效,代码中实现如下 ,保证滑动时依然有效
通过设置 self.image = image; 传入 YYAnimatedImageTypeImage type
|
|
如果当前是 YYSpriteSheetImage 会进入到 [self setContentsRect:rect forImage:newVisibleImage]; 此时设置 self.layer.contentsRect = CGRectMake(0, 0, 32 / image.size.width, 32 / image.size.height);即裁剪的第一张图像
停止动画
开始动画
自定义动画
准备工作,调用 resetAnimated, 在 dispatch_once 中初始化 _requestQueue , 设置maxConcurrentOperationCount 为1,串行执行请求图像任务,初始化_link,设置target为_YYImageWeakProxy,传入 self 赋值给 weak 属性 target,避免循环引用,添加到 mainRunLoop,mode 设置为 NSRunLoopCommonModes
开启 _link ,以大约每秒60次的频率(屏幕刷新频率)调用 step:(CADisplayLink *)link 其中 _buffer 存储图像数据, _YYAnimatedImageViewFetchOperation 用于获取图像数据 实现代码在 main 方法中的 UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
|
|
主要流程:
- 确保当前图像存在,并且循环未结束
- 如果还在当前图像显示时间内,返回
- 进入下张图像显示,如存在直接显示,并更新相应属性数据,如不存在,初始化一个operation,获取下一张图像数据
_YYAnimatedImageViewFetchOperation 实现获取下张图像
_YYAnimatedImageViewFetchOperation 是 NSOperation的子类, 用来执行获取图像操作。通过自定义main 方法实现,每添加一个 operation, _incrBufferCount ++, 遍历 _buffer, 获取丢失的图像。具体获取图像通过协议方法 animatedImageFrameAtIndex: 获取
|
|
获取图像 YYImage YYFrameImage YYSpriteSheetImage 都实现了 YYAnimatedImage 协议方法,后两个比较简单,主要来看 YYImage, 内部获取图像通过 _decoder 实现, 其中 preloadAllAnimatedImageFrames 属性可以用来预先加载所有图像数据
YYImageCoder
最终获取图像调用到 YYImageDecoder内部,返回一个 YYImageFrame实例,存储了图像信息。可以参考文章移动端图片格式调研
|
|
图像解码主要方法
根据传入的图像data 更新数据到 _frames 数组 ,里面存储的是 _YYImageDecoderFrame 。大致有三类图片数据,webP,APNG, 其他。
webP 图片通过Google的 WebP.framework 实现,APNG是 自定义实现的图像解码,其他的通过 ImageIO 框架实现的
主要看一下 _updateSourceImageIO
通过最初的图像解码得到指定Index的frame,
_YYImageDecoderFrame *frame = [(_YYImageDecoderFrame *)_frames[index] copy];
采用画布进行渲染, 最终获得图像 frame.image = image;