美文网首页
第九篇:基于定时器的动画

第九篇:基于定时器的动画

作者: 意一ineyee | 来源:发表于2018-01-22 15:26 被阅读13次

目录

一、定时器动画不用NSTimer的理由

二、使用CADisplayLink来做定时器动画

三、计算每帧的持续时间


一、定时器动画不用NSTimer的理由

  • 我们知道iOS刷新屏幕的频率是60Hz,即每秒钟刷新屏幕60次。通常情况下,如果有个基于定时器的动画,我们一般会采用NSTimer来实现,能实现效果,看起来也没什么问题,但其实对于这种基于定时器的动画,使用NSTimer并不是最佳选择。

  • 因为在前面学习runLoop的时候,我们提到过timer必须得添加到指定的runLoop模式下才能起作用,否则timer是不起作用的,而runLoop为了节省资源也并不会在非常准确的时间点调用定时器。比方说现在有一个timer的时间间隔为1s,runLoop在第一次循环中执行完timer的回调后立马进入了第二个循环,但是在第二个循环中需要做一个很大计算量的任务,计算时间超过了1s,那么timer的第二个回调就会被错过,这次回调错过了就错过了,不会说是把这次回调延后执行,而是会等到下个时间点直接执行timer的第三次回调。好,先不说计算量大的这种延时,一个runLoop通常需要处理一些手势事件、响应一些系统事件、处理定时器事件、UI的绘制与更新等,这些任务都是按着顺序执行的,也就是说timer的触发必须得等到它的上个任务完成了才会执行,因此也不能在我们设定的那个十分准确的时间点来执行事件,通常会有几毫秒的延迟,这些延时也都是一些随机值,这样即便我们设置timer每秒钟执行60次,和屏幕刷新的频率一样,也难以确保它就真得每秒钟执行60次,所以就有可能timer的触发是在屏幕刷新之后一点点,这样动画就会有个延时,看起来像是屏幕卡顿了,也有可能timer触发在屏幕刷新之前一点,紧接着下次动画,看起来像是屏幕跳动了。

  • 所以NSTimer一般用来定时一些非界面刷新的操作就可以了,界面刷新的操作用CADisplayLink比较好一点。

二、使用CADisplayLink来做定时器动画

  • 因此我们可以使用CADisplayLink来实现基于定时器的动画,CADisplayLink是Core Animation提供的一个类似于NSTimer的类,它的触发时机是每次屏幕完成刷新之前,也就是说每次屏幕要刷新的时候它都会被触发一次,因此我们就可以使用它来保证动画帧率的连续性,从而减少动画掉帧的几率,使得动画更加平滑。

举例:
(清单4.1)

//
//  ViewController.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/11/13.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

#import "ViewController.h"

#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height

@interface ViewController ()

@property (strong, nonatomic) UIView *contentView;
@property (strong, nonatomic) CAShapeLayer *layer1;

@property (strong, nonatomic) CADisplayLink *displayLink;// CADisplayLink和NSTimer差不多,注意事项也差不多
@property (assign, nonatomic) float waveAmplitude;// 振幅
@property (assign, nonatomic) float waveSpeed;// 波纹流动的速度
@property (assign, nonatomic) float waveOffset;// 初相

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initialize];
    [self layoutUI];
    [self startWave];
}


#pragma mark - private method

- (void)startWave {

    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(wave:)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)stopWave {

    [self.displayLink invalidate];
    self.displayLink = nil;
}

- (void)wave:(CADisplayLink *)displayLink {

    self.waveOffset += self.waveSpeed;

    // 绘制第一条贝塞尔曲线
    UIBezierPath *path1 = [[UIBezierPath alloc] init];
    // 起始点
    [path1 moveToPoint:CGPointMake(0, self.waveAmplitude)];// 起始点
    // 遍历所有的x坐标,根据公式求出所有的y坐标,从而得到所有的点,贝塞尔曲线将它们连起来
    for (CGFloat x = 0.0; x < kScreenWidth; x ++) {

        CGFloat y = self.waveAmplitude * sinf(3 * M_PI * x / kScreenWidth + self.waveOffset * M_PI / kScreenWidth);
        [path1 addLineToPoint:CGPointMake(x, y)];
    }
    // 将贝塞尔曲线赋值给shapeLayer的path就可以了
    self.layer1.path = path1.CGPath;
}


#pragma mark - layoutUI

- (void)layoutUI {

    [self.view addSubview:self.contentView];
    [self.contentView.layer addSublayer:self.layer1];
}


#pragma mark - setter, getter

- (UIView *)contentView {

    if (_contentView == nil) {

        _contentView = [[UIView alloc] init];
        _contentView.frame = CGRectMake(0, kScreenHeight - 200, kScreenWidth, 200);
        _contentView.backgroundColor = [UIColor yellowColor];
    }

    return _contentView;
}

- (CAShapeLayer *)layer1 {

    if (_layer1 == nil) {

        _layer1 = [CAShapeLayer layer];
        _layer1.strokeColor = [UIColor redColor].CGColor;
        _layer1.fillColor = [UIColor clearColor].CGColor;
        _layer1.lineWidth =  5;
        _layer1.lineCap = kCALineCapRound;
        _layer1.lineJoin = kCALineJoinRound;
    }

    return _layer1;
}


#pragma mark - initialize

- (void)initialize {

    // 初始值
    self.waveAmplitude = 20;
    self.waveSpeed = 2.0;
    self.waveOffset = 0.0;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
1.gif

三、计算每帧的持续时间

但即便是使用了CADisplayLink,我们也不能保证动画就不掉帧了,因为有可能一帧动画的执行时长有可能超过1/60秒,这种情况下我们就需要自己计算每帧的持续时间,代替硬编码的1/60秒了。具体学习可参看原书第11章。

相关文章

网友评论

      本文标题:第九篇:基于定时器的动画

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