FLAnimatedImage -ios gif图片加载框架介绍

简介

FLAnimatedImage 是 Flipboard 团队开发的在它们 App 中渲染 GIF 图片使用的库。 后来 Flipboard 将 FLAnimatedImage 开源出来供大家使用。本文章主要是介绍FLAnimatedImage框架的GIF动画加载和播放流程,旨在说明流程和主要细节点。

ios原有加载缺陷分析

大家知道在 iOS 中处理过 GIF 图片, 如果通过原生系统提供的能力, 可能只有两种方式。 并且这两种方式都不是专门针对于 GIF 的解决方案,更像是一种 hack。 第一种方式, UIImage 虽然提供了一种创建连续切换的动画图片的能力, 但这个能力更像是为了那些简单动画而服务的。 比如加载数据时候显示的 loading 图片。 如果将 GIF 图片通过这种能力来显示,会带来诸多问题。 第二种方式,可能是大家用的最多的了。 就是创建一个 UIWebView 然后在这里面把 GIF 显示出来。 但从原理上来想, UIWebView 并不是为了显示 GIF 图片而生的。

gif渲染原理分析

为什么说 FLAnimatedImage 相对于 iOS 原生的几种 hack 方式更趋近于最佳实践呢? 咱们简单聊聊 FLAnimatedImage 渲染 GIF 图片的原理。FLAnimatedImage 会有两个线程同时在运转。 其中一个线程负责渲染 GIF 的每一帧的图片内容(所谓的渲染,大体上就是加载 GIF 文件数据,然后抽取出来当前需要哪一帧)。这个加载图片的过程是在异步线程进行的。 然后 FLAnimatedImage 会有一个内存区域专门放置这些渲染好的帧。 这时候,在主线程中的 ImageView 会根据当前需要,从这个内存区域中读取相应的帧。这是一个典型的生产者-消费者问题。

FLAnimatedImage

FLAnimatedImage项目的流程比较简单,FLAnimatedImage就是负责GIF数据的处理,然后提供给FLAnimatedImageView一个UIImage对象。FLAnimatedImageView拿到UIImage对象显示出来就可以了。

实例

使用FLAnimatedImage 加载GIF,有三个关键函数:使用FLAnimatedImage处理GIF动画数据,使用FLAnimatedImageView展示FLAnimatedImage处理后的动画数据。

  1. 使用NSData初始化FLAnimatedImage,然后将FLAnimatedImage赋值给FLAnimatedImageView
if (!self.imageView1) {
      self.imageView1 = [[FLAnimatedImageView alloc] init];
      self.imageView1.contentMode = UIViewContentModeScaleAspectFill;
      self.imageView1.clipsToBounds = YES;
  }
  [self.view addSubview:self.imageView1];
  self.imageView1.frame = CGRectMake(0.0, 120.0, self.view.bounds.size.width, 447.0);   
  NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"rock" withExtension:@"gif"];
  NSData *data1 = [NSData dataWithContentsOfURL:url1];
  FLAnimatedImage *animatedImage1 = [FLAnimatedImage animatedImageWithGIFData:data1];
  self.imageView1.animatedImage = animatedImage1;

2.使用URL初始化FLAnimatedImage,然后将FLAnimatedImage赋值给FLAnimatedImageView

 if (!self.imageView2) {
      self.imageView2 = [[FLAnimatedImageView alloc] init];
      self.imageView2.contentMode = UIViewContentModeScaleAspectFill;
      self.imageView2.clipsToBounds = YES;
  }
  [self.view addSubview:self.imageView2];
  self.imageView2.frame = CGRectMake(0.0, 577.0, 379.0, 447.0);

  NSURL *url2 = [NSURL URLWithString:@"https://cloud.githubusercontent.com/assets/1567433/10417835/1c97e436-7052-11e5-8fb5-69373072a5a0.gif"];
  [self loadAnimatedImageWithURL:url2 completion:^(FLAnimatedImage *animatedImage) {
      self.imageView2.animatedImage = animatedImage;
  }];

FLAnimatedImage项目介绍

FLAnimatedImage项目采用了“生产者和消费者”模型来处理这个GIF动画的播放问题。一个线程负责生产数据,另一个线程负责消费数据。生产者FLAnimatedImage负责提供帧UIImage对象,消费者FLAnimatedImageView负责显示该UIImage对象。

FLAnimatedImage接口介绍

@property (nonatomic, strong, readonly) UIImage *posterImage;//GIF动画的封面帧图片
@property (nonatomic, assign, readonly) CGSize size; //GIF动画的封面帧图片的尺寸
@property (nonatomic, assign, readonly) NSUInteger loopCount; //GIF动画的循环播放次数
@property (nonatomic, strong, readonly) NSDictionary *delayTimesForIndexes; // GIF动画中的每帧图片的显示时间集合
@property (nonatomic, assign, readonly) NSUInteger frameCount; //GIF动画的帧数量
@property (nonatomic, assign, readonly) NSUInteger frameCacheSizeCurrent; //当前被缓存的帧图片的总数量
@property (nonatomic, assign) NSUInteger frameCacheSizeMax; // 允许缓存多少帧图片

// Intended to be called from main thread synchronously; will return immediately.
// If the result isn't cached, will return `nil`; the caller should then pause playback, not increment frame counter and keep polling.
// After an initial loading time, depending on `frameCacheSize`, frames should be available immediately from the cache.
// 取出对应索引的帧图片
- (UIImage *)imageLazilyCachedAtIndex:(NSUInteger)index;

// Pass either a `UIImage` or an `FLAnimatedImage` and get back its size
// 计算该帧图片的尺寸
+ (CGSize)sizeForImage:(id)image;

// 初始化方法
// On success, the initializers return an `FLAnimatedImage` with all fields initialized, on failure they return `nil` and an error will be logged.
- (instancetype)initWithAnimatedGIFData:(NSData *)data;
// Pass 0 for optimalFrameCacheSize to get the default, predrawing is enabled by default.
- (instancetype)initWithAnimatedGIFData:(NSData *)data optimalFrameCacheSize:(NSUInteger)optimalFrameCacheSize predrawingEnabled:(BOOL)isPredrawingEnabled NS_DESIGNATED_INITIALIZER;
+ (instancetype)animatedImageWithGIFData:(NSData *)data;

//初始化数据
@property (nonatomic, strong, readonly) NSData *data; // The data the receiver was initialized with; read-only

FLAnimatedImage函数解析

关键方法解析 a、对传进来的数据进行合法性判断,至少不能为nil。 b、初始化对应的变量,用于存储各类辅助数据。 c、将传进来的数据处理成图片数据,其中设置。kCGImageSourceShouldCache为NO,可以避免系统对图片进行缓存。 d、从数据中读取图片类型,判断该图片是不是GIF动画类型。 e、读取GIF动画中的动画信息,包括动画循环次数,有几帧图片等。 f、遍历GIF动画中的所有帧图片,取出并保存帧图片的播放信息,设置GIF动画的封面帧图片 g、根据设置或者GIF动画的占用内存大小,与缓存策略对比,确认缓存策略。

FLAnimatedImageView接口

@interface FLAnimatedImageView : UIImageView

// Setting `[UIImageView.image]` to a non-`nil` value clears out existing `animatedImage`.
// And vice versa, setting `animatedImage` will initially populate the `[UIImageView.image]` to its `posterImage` and then start animating and hold `currentFrame`.
@property (nonatomic, strong) FLAnimatedImage *animatedImage;//设置GIF动画数据
@property (nonatomic, copy) void(^loopCompletionBlock)(NSUInteger loopCountRemaining);//GIF动画播放一次之后的回调Block

@property (nonatomic, strong, readonly) UIImage *currentFrame;//GIF动画当前显示的帧图片
@property (nonatomic, assign, readonly) NSUInteger currentFrameIndex;//GIF动画当前显示的帧图片索引

// The animation runloop mode. Enables playback during scrolling by allowing timer events (i.e. animation) with NSRunLoopCommonModes.
// To keep scrolling smooth on single-core devices such as iPhone 3GS/4 and iPod Touch 4th gen, the default run loop mode is NSDefaultRunLoopMode. Otherwise, the default is NSDefaultRunLoopMode.
@property (nonatomic, copy) NSString *runLoopMode;

FLAnimatedImageView解析: 关键对象解析: a、判断新旧FLAnimatedImage对象是否一致,一致就不需要继续操作了 b、设置GIF动画的封面帧图片,当前帧索引,GIF动画的循环播放次数,播放时间累加器 c、更新是否发起动画的标志位,判断是否启动GIF动画 d、刷新View的layer 参考:http://swiftcafe.io/2016/12/08/fl-image/ http://engineering.flipboard.com/2014/05/animated-gif/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏邵靖的专栏

使用 plotly 绘制数据图表

不少小伙伴在开发过程中都有对模块进行压测的经历,压测结束后大家往往喜欢使用Excel处理压测数据并绘制数据可视化视图,但这样不能很方便的使用web页面进行数据展...

1.3K60
来自专栏Golang语言社区

GO语言利用K近邻算法实现小说鉴黄

Usuage: go run kNN.go --file="data.txt" 关键是向量点的选择和阈值的判定 样本数据来自国家新闻出版总署发布通知公布的《...

32150
来自专栏落影的专栏

iOS开发-视图渲染与性能优化

前言 关于iOS的视图渲染流程,以及性能优化的建议。 源于WWDC视频。 我假设你是一个这样的开发者: 了解OpenGL ES; 了解view hierar...

52970
来自专栏数据小魔方

think-cell chart系列15——蝴蝶图

今天跟大家分享think-cell chart系列的第15篇——蝴蝶图。 ? 当然think-cell chart中是无法直接制作蝴蝶图的,需要通过一对开口方向...

52040
来自专栏DannyHoo的专栏

iOS 开发中Masonry和SnapKit在使用上的一些区别

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

31720
来自专栏TechBox

UITableViewCell系列之(二)视觉差滚动效果前言

15820
来自专栏非著名程序员

代码实验室--带你一步步理解使用 ConstraintLayout

? 说明 这次 IO 给开发者带来了很多惊喜, ConstraintLayout 是其中较为实用的之一. Google 第一时间发布了官方的代码实验室指导教程...

25860
来自专栏申龙斌的程序人生

SeismicPro地震剖面显示程序

SeismicPro是我用C#写的一款地震剖面显示软件,可从标准SEGY地震数据体中抽取纵测线和横测线的二维剖面,并以波形、变面积和变密度等多种方式进行专业化显...

34590
来自专栏iOS开发攻城狮的集散地

水波进度、加载动画、文字进度

32130
来自专栏DannyHoo的专栏

iOS中将字体设置成斜体且加粗

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

52510

扫码关注云+社区

领取腾讯云代金券