iOS开发攻城狮的集散地iOS技术专题ios基础

iOS 利用CADisplayLink做逐帧动画

2018-06-04  本文已影响59人  Maj_sunshine

CADisplayLink

CADisplayLink是一个能让我们以和屏幕刷新率相同的频率将内容画到屏幕上的定时器,在以特定的mode加入runloop时,每次屏幕内容刷新结束时,runloop就会向对应的target发送一次selector方法,selector就会被调用一次。

属性 描述
timestamp 上一帧的时间戳
duration 两帧之间的时间,默认1 / 60
PreferredFramesPerSecond 每秒多少帧
paused 定时器的暂停和运行
frameInterva(iOS10弃用) 多少帧调用一次selector方法

和NSTimer区别

简单使用

添加定时器

- (void)addLink {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotate)];
        _displayLink.frameInterval = 1;
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
 // 旋转动画
- (void)rotate {
    _bgImageView.transform = CGAffineTransformRotate(_bgImageView.transform, M_PI/240);
}

销毁定时器

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if (_displayLink) {
        [_displayLink invalidate];
        _displayLink = nil;
    }
}
定时器旋转.gif

2 水波动画

其中A为振幅, 影响的是y值的增大幅度。
T = 2π/ω,ω影响的是函数的周期,越小周期越大
φ ,影响横线偏移值
k,影响y值
#import "WaveView.h"

@interface WaveView() {
    CGFloat _waveA; // 振幅
    CGFloat _waveW; // 周期w
    CGFloat _waveOffset; // 波动偏移值
    CGFloat _viewWidth; // 视图宽度
    
}
 // 定时器
@property (nonatomic, strong) CADisplayLink *displayLink;
 // layer
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
 // 贝塞尔曲线
@property (nonatomic, strong) UIBezierPath *bezierPath;

@end

@implementation WaveView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        _waveA = 20;
        _waveW = 3 / 100.0;
        _waveOffset = 0;
        _viewWidth = CGRectGetWidth(self.frame);
        
        [self setupUI];
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(waveAnimation)];
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    return self;
}

#pragma mark --- UI创建
- (void)setupUI {
    _shapeLayer = [CAShapeLayer layer];
    _shapeLayer.fillColor = [UIColor greenColor].CGColor;
    [self.layer addSublayer:_shapeLayer];
}

#pragma mark --- 刷新界面
  // 横线增加偏移,重绘
- (void)waveAnimation {
    _waveOffset += 0.15;
    [self reloadView];
}

- (void)reloadView {
    if (!_bezierPath) _bezierPath = [UIBezierPath bezierPath];
    [_bezierPath removeAllPoints];
    [_bezierPath moveToPoint:CGPointMake(_viewWidth, 200)];
    [_bezierPath addLineToPoint:CGPointMake(_viewWidth, 400)];
    [_bezierPath addLineToPoint:CGPointMake(0, 400)];
    [_bezierPath addLineToPoint:CGPointMake(0, 200)];
    CGFloat y = 300;
    for (int x = 0; x<_viewWidth; x++) {
        y = _waveA * sin(_waveW * x + _waveOffset) + 200;
        [_bezierPath addLineToPoint:CGPointMake(x, y)];
    }
    [_bezierPath closePath];
    _shapeLayer.path = _bezierPath.CGPath;
}

#pragma mark --- 定时器销毁
- (void)invalidate {
    if (!_displayLink) {
        [_displayLink invalidate];
        _displayLink = nil;
    }
}

  // 横线增加偏移,重绘
- (void)waveAnimation {
    _waveOffset += 0.15;
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(context, _viewWidth, 200);
    CGContextAddLineToPoint(context, _viewWidth, 400);
    CGContextAddLineToPoint(context, 0, 400);
    CGContextAddLineToPoint(context, 0, 200);
    CGFloat y = 300;
    for (int x = 0; x<_viewWidth; x++) {
        y = _waveA * sin(_waveW * x + _waveOffset) + 200;
        CGContextAddLineToPoint(context, x, y);
    }
    [[UIColor greenColor] setFill];
    CGContextClosePath(context);
    CGContextDrawPath(context, kCGPathFill);
}

  // 横线增加偏移,重绘
- (void)waveAnimation {
    _waveOffset += 0.15;
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    if (!_bezierPath) _bezierPath = [UIBezierPath bezierPath];
    [_bezierPath removeAllPoints];
    [[UIColor greenColor] setFill];
    [_bezierPath moveToPoint:CGPointMake(_viewWidth, 200)];
    [_bezierPath addLineToPoint:CGPointMake(_viewWidth, 400)];
    [_bezierPath addLineToPoint:CGPointMake(0, 400)];
    [_bezierPath addLineToPoint:CGPointMake(0, 200)];
    CGFloat y = 300;
    for (int x = 0; x<_viewWidth; x++) {
        y = _waveA * sin(_waveW * x + _waveOffset) + 200;
        [_bezierPath addLineToPoint:CGPointMake(x, y)];
    }
    [_bezierPath closePath];
    CGContextAddPath(context, _bezierPath.CGPath);
    CGContextDrawPath(context, kCGPathFill);
}
波浪.gif

使用drawRect绘制CPU消耗率。


屏幕快照 2018-06-04 下午3.20.24.png

再来看看使用CAShapeLayer绘制的CPU消耗


屏幕快照 2018-06-04 下午3.20.59.png

可以看出,drawRect的CPU消耗对比CAShapeLayer那是非常夸张了。drawRect果然是一个内存恶鬼。

上一篇下一篇

猜你喜欢

热点阅读