001-显示层动画

一、初级动画效果

1.基本思路

UIView的显示层初级动画就是通过修改UIView的各种属性来实现的

2.动画常用属性&动画回调方法

3.代码实例

1)位置动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(center)
_aView.center = CGPointMake(300, 600);
    
// 提交动画
[UIView commitAnimations];

2)形状动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(bounds)
_aView.bounds = CGRectMake(0, 0, 200, 200);
    
// 提交动画
[UIView commitAnimations];

3)位置+形状动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(frame)
_aView.frame = CGRectMake(100, 400, 200, 200);
    
// 提交动画
[UIView commitAnimations];

4)淡入淡出动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(alpha)
_aView.alpha = 1;
    
// 提交动画
[UIView commitAnimations];

5)颜色渐变动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(backgroundColor)
_aView.backgroundColor = [UIColor blueColor];
    
// 提交动画
[UIView commitAnimations];

6)缩放动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(transform)
_aView.transform = CGAffineTransformMakeScale(2.0, 3.0);
    
// 提交动画
[UIView commitAnimations];

7)旋转动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(transform)
_aView.transform = CGAffineTransformMakeRotation(M_PI_4);
    
// 提交动画
[UIView commitAnimations];

8)位移动画

// 开始动画
[UIView beginAnimations:nil context:nil];
    
// 设置动画属性
[UIView setAnimationDuration:1.0];
    
// 修改属性(transform)
_aView.transform = CGAffineTransformMakeTranslation(150, 300);
    
// 提交动画
[UIView commitAnimations];

 

二、关键帧动画

1.方法解读

2.代码实例

// 关键帧动画
[UIView animateKeyframesWithDuration:4 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
    // 添加关键帧动画
        
    // 第一帧:动画从0s开始,持续整个动画周期(4s)的1/2时间(2s)
    [UIView addKeyframeWithRelativeStartTime:0 relativeDuration:1.0 / 2.0 animations:^{
      // 改变动画属性
      _imageView.frame = CGRectMake(200, 100, 60, 60);
    }];
        
    // 第二帧:动画从整个动画周期(4s)的1/2时间(2s)开始,持续整个动画周期(4s)的1/2时间(2s)
    [UIView addKeyframeWithRelativeStartTime:1.0 / 2.0 relativeDuration:1.0 / 2.0 animations:^{
      // 改变动画属性
      _imageView.frame = CGRectMake(300, 300, 90, 90);
    }];
} completion:nil];

 

三、逐帧动画

1.基于NSTimer的逐帧动画

1)原理

利用定时器NSTimer去定时更换每一帧的图片;一般用于逐帧动画时间间隔比较长的时候

2)实例代码

2.基于CADisplayLink的逐帧动画

1)原理

利用定时器NSTimer去定时更换每一帧的图片;一般用于逐帧动画时间间隔比较短(超过每秒60帧)的时候,或者要求播放帧率之间间隔比较均匀

2)CADisplayLink & NSTimer的区别

iOS设备的屏幕刷帧频率默认是60Hz(即1s刷帧60次),而CADisplayLink默认的刷帧频率刚好与之保持一致,因此CADisplayLink的精度特别高

3)代码实例

#pragma mark - 界面
- (void)initUI
{
    // imageView
    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 300, 90, 90)];
    _imageView.image = [UIImage imageNamed:@"image_0"];
    _imageView.contentMode = UIViewContentModeScaleAspectFit;
    [self.view addSubview:_imageView];
    
    // 定义CADisplayLink
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(refreshImageView)];
    _displayLink.frameInterval = 1;
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    _index = 0;
}

#pragma mark - CADisplayLink事件
- (void)refreshImageView
{
    _imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"image_%d", _index]];
    _index++;
    if (_index == 12) {
        [_displayLink invalidate];
        _displayLink = nil;
    }
}

3.基于draw方法的逐帧动画

1)draw方法

a.当创建一个新的UIView时,其自动生成了一个draw()方法,且此方法可以被重写

b. draw方法一般什么时候调用:

①使用addSubview会触发layoutSubviews

②更新view的frame属性会触发layoutSubviews

③直接调用setLayoutSubviews方法会触发layoutSubviews

2)代码实例

// ViewController.m文件
#pragma mark - 界面
- (void)initUI
{
    // 自定义bView(待绘制的view)
    _bView = [[LDView alloc] initWithFrame:self.view.bounds];
    _bView.backgroundColor = [UIColor whiteColor];
    _radius = 0;
    [self.view addSubview:_bView];
    
    // 动画btn
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
    btn.frame = CGRectMake(self.view.bounds.size.width / 2.0, self.view.bounds.size.height - 40, 100, 30);
    [btn setTitle:@"动画" forState:UIControlStateNormal];
    [btn setTitle:@"停止动画" forState:UIControlStateSelected];
    [btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

#pragma mark - 点击事件
- (void)btnClick:(UIButton *)sender
{
    sender.selected = !sender.isSelected;
    if (sender.isSelected) {
        // 定义定时器
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(refreshImageView) userInfo:nil repeats:YES];
    } else {
        [_timer invalidate];
        _timer = nil;
        _radius = 0;
    }
}

#pragma mark - CADisplayLink事件
- (void)refreshImageView
{
    _bView.radius = _radius++;
}

// LDView.h文件
#import <UIKit/UIKit.h>

@interface LDView : UIView

#pragma mark - 属性
@property (nonatomic, assign) CGFloat radius;

@end

// LDView.m文件
#import "LDView.h"

@implementation LDView

#pragma mark - 属性的setter方法
- (void)setRadius:(CGFloat)radius
{
    _radius = radius;
    // 重新绘制(调用drawRect:方法)
    [self setNeedsDisplay];
}

#pragma mark - 重写
- (void)drawRect:(CGRect)rect
{
    // 获取上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 绘制圆
    CGContextAddArc(ctx, self.bounds.size.width / 2.0, self.bounds.size.height / 2.0, self.radius, 0, M_PI * 2, NO);
    // 渲染
    CGContextFillPath(ctx);
}

 

四、GIF动画

1.GIF分解单帧图片

1)GIF图片分解过程

①本地读取GIF图片,将其转换成NSData类型

②将NSData作为ImageIO模块的输入

③获取ImageIO的输出数据:UIImage

④将获取的UIImage数据存储为JPG或者PNG格式保存到本地

2)代码实例

#pragma mark - GIF分解单帧图片
- (void)resolveGIF
{
    // 1.本地读取GIF图片,将其转换成NSData类型
    NSString *gifName = [[NSBundle mainBundle] pathForResource:@"53c380a7a2ed8" ofType:@"gif"];
    NSData *gifData = [NSData dataWithContentsOfFile:gifName];
    
    // 2.将NSData作为ImageIO模块的输入
    CGImageSourceRef gifDataSourceRef = CGImageSourceCreateWithData((CFDataRef)gifData, nil);
    NSInteger gifDataCount = CGImageSourceGetCount(gifDataSourceRef);
    for (NSInteger i = 0; i < gifDataCount - 1; i++) {
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifDataSourceRef, i, nil);
        
        // 3.获取ImageIO的输出数据:UIImage
        UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
        
        // 4.将获取的UIImage数据存储为JPG或者PNG格式保存到本地
        NSData *imageData = UIImagePNGRepresentation(image);
        NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:[NSString stringWithFormat:@"%ld.png", i]];
        NSError *error = nil;
        [imageData writeToFile:filePath options:NSDataWritingAtomic error:&error];
        if (!error) {
            NSLog(@"第%ld张图片保存成功!", i);
        } else {
            NSLog(@"第%ld张图片保存失败!", i);
        }
    }
}

2.单帧图片合成GIF

1)合成GIF的过程

①加载待处理的n张图片原始数据源

②在Document目录下构建GIF文件

③循环为GIF图像对象添加每一帧元素

④设置GIF文件属性,利用ImageIO编码GIF文件

2)代码实例

#pragma mark - 单帧图片合成GIF
- (void)compoundGIF
{
    // 1.加载待处理的n张图片原始数据源
    NSMutableArray *images = [NSMutableArray array];
    for (NSInteger i = 0; i < 30; i++) {
        NSString *imageName = [NSString stringWithFormat:@"%ld.png", i];
        UIImage *image = [UIImage imageNamed:imageName];
        [images addObject:image];
    }
    
    // 2.在Document目录下构建GIF文件
    NSString *gifPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"hudie.gif"];
    CFURLRef gifURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)gifPath, kCFURLPOSIXPathStyle, NO);
    CGImageDestinationRef imageDestinationRef = CGImageDestinationCreateWithURL(gifURL, (CFStringRef)@"com.compuserve.gif", images.count, nil);
    
    // 3.循环为GIF图像对象添加每一帧元素
    // 设置每帧图片之间的间隔
    NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.2f] forKey:(NSString *)kCGImagePropertyGIFDelayTime] forKey:(NSString *)kCGImagePropertyGIFDictionary];
    // 循环为GIF图像对象添加每一帧元素
    for (UIImage *image in images) {
        CGImageDestinationAddImage(imageDestinationRef, image.CGImage, (CFDictionaryRef)properties);
    }
    
    // 4.设置GIF文件属性,利用ImageIO编码GIF文件
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];
    // 设置图片彩色空间格式
    [dic setValue:(NSString *)kCGImagePropertyColorModelRGB forKey:(NSString *)kCGImagePropertyColorModel];
    // 设置图片的颜色深度
    [dic setValue:@16 forKey:(NSString *)kCGImagePropertyDepth];
    // 设置图片的GIF执行次数
    [dic setValue:@1 forKey:(NSString *)kCGImagePropertyGIFLoopCount];
    NSDictionary *propertyGIFDictionary = [NSDictionary dictionaryWithObject:dic forKey:(NSString *)kCGImagePropertyGIFDictionary];
    CGImageDestinationSetProperties(imageDestinationRef, (CFDictionaryRef)propertyGIFDictionary);
    // 完成GIF的Destination目标文件构建
    CGImageDestinationFinalize(imageDestinationRef);
}

3.GIF图像展示

1)原理

iOS原生的其实是不支持直接显示GIF图片,但是我们知道:GIF其实就是一帧帧的单帧图片构成,所以我们可以先对GIF分解,接下来再去进行多图片显示

GIF展示步骤:

①GIF分解

②进行多图片显示

2)代码实例

#pragma mark - GIF图像展示
- (void)showGIF
{
    // 1.GIF分解单帧图片
    NSMutableArray *images = [NSMutableArray array];
    // 本地读取GIF图片,将其转换成NSData类型
    NSString *gifName = [[NSBundle mainBundle] pathForResource:@"53c380a7a2ed8" ofType:@"gif"];
    NSData *gifData = [NSData dataWithContentsOfFile:gifName];
    // 将NSData作为ImageIO模块的输入
    CGImageSourceRef gifDataSourceRef = CGImageSourceCreateWithData((CFDataRef)gifData, nil);
    NSInteger gifDataCount = CGImageSourceGetCount(gifDataSourceRef);
    for (NSInteger i = 0; i < gifDataCount - 1; i++) {
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(gifDataSourceRef, i, nil);
        // 获取ImageIO的输出数据:UIImage
        UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
        [images addObject:image];
    }
    
    // 2.借助UIImageView展示单帧图片
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
    imageView.contentMode = UIViewContentModeCenter;
    [self.view addSubview:imageView];
    // 设置图片数组
    imageView.animationImages = images;
    // 设置总时长
    imageView.animationDuration = 5;
    // 设置展示次数
    imageView.animationRepeatCount = 1;
    [imageView startAnimating];
}

 

posted @ 2017-11-16 12:06  Frank9098  阅读(154)  评论(0)    收藏  举报