FLAnimatedImageView处理gif过程

时间控制原理

GIF图片每一帧的delayTime可能都不一样;

在展示下一帧的时间控制机制,不能根据以第一帧为准;

或总动画时长除以帧数来简单做平均值为准,

都是不太好的方案。

FLAnimatedImageView的控制方式,读取每一帧的delayTime算出最大公约数,用CADisplayLink来控制时间的,比如说(如下图),

第二帧到第三帧的控制:第二帧的delayTime=2s,第三帧的delayTime=3s,如果第二帧没到时间,FLAnimatedImageView的image数据保持不变,时间一到就从FLAnimatedImage中获取image,赋值给ImageView。

CADisplayLink处理过程

首先startAnimating中的CADisplayLink初始化,为了防止retain cycle 用了NSProxy weak语义的property,也可以用

__weak typeof(self) wself = self;
...
// block中处理
__strong typeof(wself) sself = wself;
if (!sself) {
return;
}
// 紧跟处理code

反正都与weak有关,都是固定套路了。

CADisplayLink的frameInterval,frameInterval = 1时,refreshRate = 60Hz,frameInterval = 2时,refreshRate = 30Hz; 
此处设置的

const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz

// 最小的frameInterval = 1
self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, );

接下来就是最关键的处理方法- (void)displayDidRefresh:(CADisplayLink *)displayLink

1,如果self.needsDisplayWhenImageBecomesAvailable==YES,调用[self.layer setNeedsDisplay];,标记layer需要刷新,下一次runloop中displayLayer方法会被调用;然后设置self.needsDisplayWhenImageBecomesAvailable=NO不到下一帧的时间,不改变ImageView的内容; 
赋值代码相当简单:

- (void)displayLayer:(CALayer *)layer
{
// 从 image 方法中取currentFrame作为像是内容
layer.contents = (__bridge id)self.image.CGImage;
}

2,累加时间值accumulator跟当前帧的delayTime比对,注释如下:

/**
* duration属性提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间,一个略小的值;
* frameInterval相当一个>=1的固定值,每次refresh,accumulator都累加一小段时间
**/
self.accumulator += displayLink.duration * displayLink.frameInterval; // While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m /**
* 1,如果self.accumulator < 当前的delayTime,直接跳过,下一次refresh,accumulator再累加,相当于这一次image的内容不变;
* 2,只有当累加值self.accumulator >= 当前的delayTime(说明已经是累加到这一帧的delayTime的最后,下一帧的开头了),进入while循环,循环内部每次将累加器减掉delayTime(accumulator又回到0状态),以便跳出循环;currentFrameIndex累加,下次refresh时,获取下一帧图像数据;
* 3,如果到达最后一帧,循环次数loopCountdown--,又跳转到首帧图像;
* 4,最主要的一步标记self.needsDisplayWhenImageBecomesAvailable = YES;下一次refresh时,执行[self.layer setNeedsDisplay]; 进行新一帧图像数据的刷新。
**/
while (self.accumulator >= delayTime) {
self.accumulator -= delayTime;
self.currentFrameIndex++;
if (self.currentFrameIndex >= self.animatedImage.frameCount) {
// If we've looped the number of times that this animated image describes, stop looping.
self.loopCountdown--;
if (self.loopCompletionBlock) {
self.loopCompletionBlock(self.loopCountdown);
} if (self.loopCountdown == ) {
[self stopAnimating];
return;
}
self.currentFrameIndex = ;
}
// Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to.
// Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded.
self.needsDisplayWhenImageBecomesAvailable = YES;
}

至此就时间控制过程基本处理完成。 
另外FLAnimatedImage类,专门根据gif图片大小来限制缓存多少帧,当遇到memoryWarning时,如何重试处理;多次遇到memoryWarning,固定缓存帧数等等功能,而且没有用到dispatch_semaphore等加锁/解锁操作;性能有保证。

FLAnimatedImageView处理gif过程的更多相关文章

  1. c++ primer plus 第6版 部分二 5- 8章

    ---恢复内容开始--- c++ primer plus 第6版 部分二    5-  章 第五章 计算机除了存储外 还可以对数据进行分析.合并.重组.抽取.修改.推断.合成.以及其他操作 1.for ...

  2. 从源码看Azkaban作业流下发过程

    上一篇零散地罗列了看源码时记录的一些类的信息,这篇完整介绍一个作业流在Azkaban中的执行过程,希望可以帮助刚刚接手Azkaban相关工作的开发.测试. 一.Azkaban简介 Azkaban作为开 ...

  3. DBImport V3.7版本发布及软件稳定性(自动退出问题)解决过程分享

    DBImport V3.7介绍: 1:先上图,再介绍亮点功能: 主要的升级功能为: 1:增加(Truncate Table)清表再插入功能: 清掉再插,可以保证两个库的数据一致,自己很喜欢这个功能. ...

  4. HTML渲染过程详解

    无意中看到寒冬关于前端的九个问题,细细想来我也只是对第一.二.九问有所了解,正好也趁着这个机会梳理一下自己的知识体系.由于本人对http协议以及dns对url的解析问题并不了解,所以这里之探讨url请 ...

  5. iOS可视化动态绘制八种排序过程

    前面几篇博客都是关于排序的,在之前陆陆续续发布的博客中,我们先后介绍了冒泡排序.选择排序.插入排序.希尔排序.堆排序.归并排序以及快速排序.俗话说的好,做事儿要善始善终,本篇博客就算是对之前那几篇博客 ...

  6. lua执行字节码的过程介绍

    前面一篇文章中介绍了lua给下面代码生成最终的字节码的整个过程,这次我们来看看lua vm执行这些字节码的过程. foo = "bar" local a, b = "a& ...

  7. centos7+mono4+jexus5.6.2安装过程中的遇到的问题

    过程参考: http://www.linuxdot.net/ http://www.jexus.org/ http://www.mono-project.com/docs/getting-starte ...

  8. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  9. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

随机推荐

  1. cocos2d-x - C++/Lua交互

    使用tolua++将自定义的C++类嵌入,让lua脚本使用 一般过程: 自定义类 -> 使用tolua++工具编译到LuaCoco2d.cpp中 -> lua调用 步骤一:自定义一个C++ ...

  2. python3和python2的区别部分

    字典没有iteritems(),只有items() py2items()返回的是列表,iteritems()返回的是迭代器 py3items()返回的是迭代器

  3. Golang 微信机器人包

    一. 最近用在学习golang,写了个小工具练练手.通过golang模拟微信网页端,接收微信服务器的消息并定制.可接入图灵机器人的api实现一个微信机器人的小玩具,当然了,可以有更多更好玩的玩法. 二 ...

  4. 何为代理?jdk动态代理与cglib代理、spring Aop代理原理浅析

    原创声明:本博客来源为本人原创作品,绝非他处摘取,转摘请联系博主 代理(proxy)的定义:为某对象提供代理服务,拥有操作代理对象的功能,在某些情况下,当客户不想或者不能直接引用另一个对象,而代理对象 ...

  5. 【openstack N版】——摘除一个计算节点

    1.查看计算节点 #查看所有计算节点 [root@open-control01 ~]# nova service-list+----+------------------+-------------- ...

  6. AspNetCore-MVC实战系列(四)之结尾

    AspNetCore - MVC实战系列目录 . 爱留图网站诞生 . git源码:https://github.com/shenniubuxing3/LovePicture.Web . AspNetC ...

  7. WebGIS开源解决方案之矢量数据导入

    前几篇介绍了开源WebGIS开发环境的搭建,本篇开始陆续介绍这些软件的使用,WebGIS的开发,首要的问题是解决数据来源,本篇主要介绍矢量数据在开源空间数据库PostgreSQL中的存储.后续篇幅中再 ...

  8. 2017Unity开发者大会备受关注的原因有哪些?

    Unite大会是由Unity举办的全球开发者大会,至今已有10年的历史.从最开始Unity开发者大会仅500人,到现在Unity大会已经增长到5000人,10倍的参与人数,Unity开发者大会仅仅用了 ...

  9. mysql varchar类型转换int类型找出最大值

    (1) 不严谨的,最简单的 select MAX(字段名 + 0) from 表名; (2) 使用函数实现 select MAX(cast(字段名 as SIGNED INTEGER)) from 表 ...

  10. [刷题]算法竞赛入门经典 3-1/UVa1585 3-2/UVa1586 3-3/UVa1225

    书上具体所有题目:http://pan.baidu.com/s/1hssH0KO(我也是在网上找到的pdf,但不记得是从哪里搜刮到的了,就重新上传了一遍) PS:第一次写博客分享我的代码,不知道我对c ...