实现动画方式深度解析(一) —— 播放GIF动画(一)

版本记录

版本号 时间
V1.0 2017.09.16

前言

app中好的炫的动画可以让用户耳目一新,为产品增色不少,关于动画的实现我们可以用基本动画、关键帧动画、序列帧动画以及基于CoreGraphic的动画等等,接下来这几篇我就介绍下我可以想到的几种动画绘制方法。具体Demo示例已开源到Github —— 刀客传奇

ios中实现动画的几种方式

  • GIF

    • 这个就很简单了,让UI给你个Gif图,你就直播播放就可以了。具体播放可是使用UIWebView、UIImageView以及框架FLAnimatedImage
  • 序列帧动画

    • 将动画的每一帧都放在本地,然后用UIImageView播放序列帧动画。
  • 系统的框架CoreAnimation

    • 里面可以做关键帧动画,基本动画等等。下面会给出CoreAnimation的框架结构图,关于CoreAnimation框架我会分一大块去讲解。
  • 系统自带的UIView动画

    • 直接在block里面实现位置大小改变等操作,并可设置动画结束后的逻辑,可做一些简单的动画。
  • 第三方框架比如说Lottie - ios

  • 第三方框架比如说Facebook Keyframes

    • 关键帧是Facebook构建的一个非常好的新库。 然而,关键帧不支持一些Lottie的功能,如遮罩,修剪路径等等。
  • CoreGraphic

    • 利用CoreGraphic和时间控制,可以自己自定义设计动画,这种方式不是很好把握,但是还是可以实现的。
CoreAnimation

上面已经列出来了,我认为很全的做动画的几种方式或者思考方法,不足的或者疏漏的希望大家提醒我,我好补全。


播放GIF动画

播放GIF动画的方式有很多,下面我就主要介绍几种方式。首先准备了点GiF素材,如下。

1. 利用WebView播放GIF动画

利用系统自带的WebView就可以加载数据播放动画。主要思路步骤如下:

  • 找到gif文件在bundle的地址路径
  • 利用NSData的类方法,将gif数据类型改变
  • 利用UIWebView对象方法loadData:加载数据就可以播放了。

具体代码如下所示:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0.0, 0.0, 375, 667)];
    webView.scalesPageToFit = YES;
    webView.opaque = NO;
    [self.view addSubview:webView];
    
    //找到路径文件
    NSString *pathStr = [[NSBundle mainBundle] pathForResource:@"gifAnimation.gif" ofType:nil];
    
    //将gif转化为NSData数据
    NSData *gifData = [NSData dataWithContentsOfFile:pathStr];
    
    //将gifData给WebView进行播放
    [webView loadData:gifData MIMEType:@"image/gif" textEncodingName:nil baseURL:nil];
}

@end

下面看一下播放效果

可见可以正常播放,具体界面适配问题就不说了,能播放就可以。

2. 利用UIImageView和ImageIO框架播放

具体思路就是将gif转化为多张静态png图片,然后利用UIImageView播放。需要借助框架#import <ImageIO/ImageIO.h>里面的接口实现。

下面我们就看一下代码

#import "ViewController.h"
#import <ImageIO/ImageIO.h>

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //gif URL路径
    NSURL *gifURL = [[NSBundle mainBundle] URLForResource:@"gifAnimation" withExtension:@"gif"];
    
    //gif转图片
    CGImageSourceRef gifSource = CGImageSourceCreateWithURL((CFURLRef)gifURL, NULL);
    
    //图片个数
    size_t frameCount = CGImageSourceGetCount(gifSource);
    
    //将CGImage转化为UIImage,并存储在数组里面
    NSMutableArray *frameArrM = [NSMutableArray array];
    for (size_t i = 0; i < frameCount; i ++) {
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifSource, i, NULL);
        UIImage *image = [UIImage imageWithCGImage:imageRef];
        [frameArrM addObject:image];
        CGImageRelease(imageRef);
    }
    
    //动画显示
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:imageView];
    imageView.animationImages = [frameArrM copy];
    imageView.animationDuration = 1/10;
    [imageView startAnimating];
}

@end

下面看一下效果验证。

UIImageView播放是通过定时器来控制图片模拟动画的,它们控制的桢速是固定的。如果设置的模拟桢速跟gif本身的桢速相近的话倒没什么,如果桢速相差过大就会产生卡顿或者快进的视觉效果。

3. SDWebImage

这个网络图片加载框架,大家都很清楚了,和gif相关的类只有UIImage+GIF,它是UIImage的一个分类。

看一下这个分类的接口

#import "SDWebImageCompat.h"

@interface UIImage (GIF)

/**
 *  Compatibility method - creates an animated UIImage from an NSData, it will only contain the 1st frame image
 */
+ (UIImage *)sd_animatedGIFWithData:(NSData *)data;

/**
 *  Checks if an UIImage instance is a GIF. Will use the `images` array
 */
- (BOOL)isGIF;

@end

这个接口只提供了两个方法:

  • + (UIImage *)sd_animatedGIFWithData:(NSData *)data;,这个方法只会返回第一帧。
  • - (BOOL)isGIF;判断UIImage实例是否是Gif。

下面看一下具体实现

#import "UIImage+GIF.h"
#import <ImageIO/ImageIO.h>
#import "objc/runtime.h"
#import "NSImage+WebCache.h"

@implementation UIImage (GIF)

+ (UIImage *)sd_animatedGIFWithData:(NSData *)data {
    if (!data) {
        return nil;
    }

    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);

    size_t count = CGImageSourceGetCount(source);

    UIImage *staticImage;

    if (count <= 1) {
        staticImage = [[UIImage alloc] initWithData:data];
    } else {
        // we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
        // this here is only code to allow drawing animated images as static ones
#if SD_WATCH
        CGFloat scale = 1;
        scale = [WKInterfaceDevice currentDevice].screenScale;
#elif SD_UIKIT
        CGFloat scale = 1;
        scale = [UIScreen mainScreen].scale;
#endif
        
        CGImageRef CGImage = CGImageSourceCreateImageAtIndex(source, 0, NULL);
#if SD_UIKIT || SD_WATCH
        UIImage *frameImage = [UIImage imageWithCGImage:CGImage scale:scale orientation:UIImageOrientationUp];
        staticImage = [UIImage animatedImageWithImages:@[frameImage] duration:0.0f];
#elif SD_MAC
        staticImage = [[UIImage alloc] initWithCGImage:CGImage size:NSZeroSize];
#endif
        CGImageRelease(CGImage);
    }

    CFRelease(source);

    return staticImage;
}

- (BOOL)isGIF {
    return (self.images != nil);
}

@end

调用上面的接口只能返回第一帧。所以不可以的,我查了下资料,SDWebImage以前是可以的,但是后来更改了接口就不可以了。

下面我们就验证这个问题,看一下代码

#import "ViewController.h"
#import "SDWebImage/UIImage+GIF.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //找到路径文件
    NSString *pathStr = [[NSBundle mainBundle] pathForResource:@"gifAnimation.gif" ofType:nil];
    
    //将gif转化为NSData数据
    NSData *gifData = [NSData dataWithContentsOfFile:pathStr];
    
    UIImage *image = [UIImage sd_animatedGIFWithData:gifData];
    
    //显示
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.frame];
    [self.view addSubview:imageView];
    imageView.image = image;
}

@end

运行起来就会发现,只是一张静态的图片,并不能播放GIF。为什么会这样呢?

看一下UIImage+GIF.mcount > 1时给的提示:

//we will only retrieve the 1st frame. the full GIF support is available via the FLAnimatedImageView category.
// this here is only code to allow drawing animated images as static ones

意思就是让我们使用FLAnimatedImageView这个框架和接口播放GIF。

所以,利用SDWebImage框架播放GIF是行不通的了。

4. FLAnimatedImage

FLAnimatedImage 是由Flipboard开源的iOS平台上播放GIF动画的一个优秀解决方案,在内存占用和播放体验都有不错的表现。

这里就简单的提一下,详细的说明放在下篇,避免内容混杂,保持逻辑的清晰性,希望大家持续关注我。

后记

未完,待续~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容