版本记录
版本号 | 时间 |
---|---|
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
的功能,如遮罩,修剪路径等等。
- 关键帧是Facebook构建的一个非常好的新库。 然而,关键帧不支持一些
-
CoreGraphic
- 利用
CoreGraphic
和时间控制,可以自己自定义设计动画,这种方式不是很好把握,但是还是可以实现的。
- 利用
上面已经列出来了,我认为很全的做动画的几种方式或者思考方法,不足的或者疏漏的希望大家提醒我,我好补全。
播放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
下面看一下效果验证。
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。为什么会这样呢?
看一下UIImage+GIF.m
中count > 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动画的一个优秀解决方案,在内存占用和播放体验都有不错的表现。
这里就简单的提一下,详细的说明放在下篇,避免内容混杂,保持逻辑的清晰性,希望大家持续关注我。
后记
未完,待续~~