实现动画方式深度解析(一) —— 播放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

    • Airbub开发的一个动画框架,具体可以参考一种动画框架Lottie的解析(三)—— 框架结构。
  • 第三方框架比如说Facebook Keyframes,

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

    • 利用CoreGraphic和时间控制,可以自己自定义设计动画,这种方式不是很好把握,但是还是可以实现的。
实现动画方式深度解析(一) —— 播放GIF动画(一)_第1张图片
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 里面的接口实现。

下面我们就看一下代码

#import "ViewController.h"
#import 

@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

下面看一下效果验证。

实现动画方式深度解析(一) —— 播放GIF动画(一)_第2张图片

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 
#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。为什么会这样呢?

实现动画方式深度解析(一) —— 播放GIF动画(一)_第3张图片

看一下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动画的一个优秀解决方案,在内存占用和播放体验都有不错的表现。

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

后记

未完,待续~~

实现动画方式深度解析(一) —— 播放GIF动画(一)_第4张图片

你可能感兴趣的:(实现动画方式深度解析(一) —— 播放GIF动画(一))