美文网首页iOS从头开始做一个APP
实现动画方式深度解析(一) —— 播放GIF动画(一)

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

作者: 刀客传奇 | 来源:发表于2017-09-17 12:36 被阅读106次

    版本记录

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

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

    后记

    未完,待续~~

    相关文章

      网友评论

      本文标题:实现动画方式深度解析(一) —— 播放GIF动画(一)

      本文链接:https://www.haomeiwen.com/subject/yuwusxtx.html