美文网首页
第五篇:CALayer能力之专用图层

第五篇:CALayer能力之专用图层

作者: 意一ineyee | 来源:发表于2018-01-22 17:08 被阅读37次

目录

一、CAShapeLayer结合UIBezierPath绘制任意形状的东西

二、CAGradientLayer颜色渐变

三、其它layer


一、CAShapeLayer结合UIBezierPath绘制任意形状的东西

简单说一下位图(bit map)和矢量图(vector map):

  • 位图是用像素点表现的图像,拉伸的话会失真;
  • 矢量图是用数学表达式表现的图像,拉伸的时候不会失真。

CAShapeLayer就是一个用矢量图来绘制图形的专用图层,我们只需要给定它的颜色、宽度等属性,然后用UIBezierPath指定随便一个你想要的path,并把这个BezierPath赋值给shapeLayer的path,它就能自动渲染出我们想要的任何图形了。而BezierPath绘制path无论是指定点还是通过数学公式计算出来的点,其本质都是找到一个一个的点,用BezierPath把它们串起来。

接下来,我们将通过三个例子,来看一下它怎么使用:

  • 1、画个小人
  • 2、画折线图
  • 3、QQ电话那种波浪效果
1、画个小人
//
//  ViewController.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/11/13.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
        
    // 创建shapeLayer
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.strokeColor = [UIColor redColor].CGColor;// 路径的颜色
    shapeLayer.fillColor = [UIColor orangeColor].CGColor;// 路径的空白部分的填充色
    shapeLayer.lineWidth = 5;// 路径的宽度
    shapeLayer.lineCap = kCALineCapSquare;// 路径结尾的样子
    shapeLayer.lineJoin = kCALineJoinRound;// 路径结合点的样子
    [self.view.layer addSublayer:shapeLayer];
    
    // 绘制贝塞尔路径
    UIBezierPath *path = [[UIBezierPath alloc] init];
    [path moveToPoint:CGPointMake(125, 100)];
    [path addArcWithCenter:CGPointMake(100, 100) radius:25 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    [path moveToPoint:CGPointMake(100, 125)];
    [path addLineToPoint:CGPointMake(100, 175)];
    [path addLineToPoint:CGPointMake(125, 200)];
    [path moveToPoint:CGPointMake(100, 175)];
    [path addLineToPoint:CGPointMake(75, 200)];
    [path moveToPoint:CGPointMake(50, 150)];
    [path addLineToPoint:CGPointMake(150, 150)];
    
    // 贝塞尔路径赋值给shapeLayer的path属性
    shapeLayer.path = path.CGPath;
}


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

@end

效果:


1.png
2、绘制折线图
//
//  ViewController.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/11/13.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

#import "ViewController.h"

#define kLineChartWidth 300
#define kLineChartHeight 200

@interface ViewController ()

@property (strong, nonatomic) UIView *lineChartView;
@property (strong, nonatomic) CAShapeLayer *lineLayer;
@property (strong, nonatomic) UIBezierPath *bezierPath;

// 点
@property (copy,   nonatomic) NSArray *xArray;
@property (copy,   nonatomic) NSArray *yArray;

// 倍率
@property (assign, nonatomic) CGFloat xBL;
@property (assign, nonatomic) CGFloat yBL;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initialize];
    [self layoutUI];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self drawLine];
    });
}


#pragma mark - private method

// 绘制折线
- (void)drawLine {
    
    [self.lineChartView.layer addSublayer:self.lineLayer];
}

// 绘制X轴
- (void)drawXAxis {
    
    CALayer *xAxisLayer = [[CALayer alloc] init];
    xAxisLayer.frame = CGRectMake(0, kLineChartHeight - 3, kLineChartWidth, 3);
    xAxisLayer.backgroundColor = [UIColor lightGrayColor].CGColor;
    [self.lineChartView.layer addSublayer:xAxisLayer];
}

// 绘制垂直于X轴的线条,假设我们需要平均分成10份
- (void)drawXAxisLine {
    
    CGFloat unitGap = kLineChartWidth / 10;// X轴单位间隔
    for (int i = 0; i < 11; i ++) {

        CALayer *tempLayer = [CALayer layer];
        tempLayer.frame = CGRectMake(unitGap * i, 0, 2, kLineChartHeight);
        tempLayer.backgroundColor = [UIColor lightGrayColor].CGColor;
        [self.lineChartView.layer addSublayer:tempLayer];
    }
}


#pragma mark - layoutUI

- (void)layoutUI {
    
    [self.view addSubview:self.lineChartView];
    
    [self drawXAxis];
    [self drawXAxisLine];
}


#pragma mark - setter, getter

- (UIView *)lineChartView {
    
    if (_lineChartView == nil) {
        
        _lineChartView = [[UIView alloc] init];
        _lineChartView.frame = CGRectMake(50, 250, kLineChartWidth, kLineChartHeight);
        _lineChartView.backgroundColor = [UIColor yellowColor];
    }
    
    return _lineChartView;
}

- (CAShapeLayer *)lineLayer {
    
    if (!_lineLayer) {
        
        _lineLayer = [CAShapeLayer layer];
        _lineLayer.strokeColor = [UIColor orangeColor].CGColor;
        _lineLayer.fillColor = [UIColor clearColor].CGColor;
        _lineLayer.lineWidth =  5;
        _lineLayer.lineCap = kCALineCapRound;
        _lineLayer.lineJoin = kCALineJoinRound;
        
        // 将贝塞尔路径赋值给shapeLayer的path
        _lineLayer.path = self.bezierPath.CGPath;
        
        // 创建一个动画
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        animation.fromValue = @(0.0);
        animation.toValue = @(3.0);
        animation.autoreverses = NO;
        animation.duration = 5;
        
        // 给折线 layer 添加动画
        [_lineLayer addAnimation:animation forKey:nil];
        _lineLayer.strokeEnd = 1;
    }
    
    return _lineLayer;
}

- (UIBezierPath *)bezierPath {
    
    if (_bezierPath == nil) {
        
        _bezierPath = [[UIBezierPath alloc] init];
        
        for (int i = 0; i < self.xArray.count; i ++) {
            
            CGFloat X = [self.xArray[i] floatValue] * self.xBL;
            CGFloat Y = kLineChartHeight - [self.yArray[i] floatValue] * self.yBL;
            
            CGPoint tempPoint = CGPointMake(X, Y);
            
            if (i == 0) {
                
                [_bezierPath moveToPoint:tempPoint];// 设置起点
            }
            
            [_bezierPath addLineToPoint:tempPoint];
        }
    }
    
    return _bezierPath;
}


#pragma mark - initialize

- (void)initialize {
    
    // 点
    self.xArray = @[@"0", @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"];
    self.yArray = @[@"3.4", @"2.9", @"1.05", @"0", @"0", @"1.7", @"6.3", @"3.4", @"5.5", @"2.4"];
    
    // X轴和Y轴的倍率(即X轴和Y轴上每一份代表实际多大)
    CGFloat maxX = [[self.xArray valueForKeyPath:@"@max.floatValue"] floatValue];
    self.xBL = kLineChartWidth / maxX;
    CGFloat maxY = [[self.yArray valueForKeyPath:@"@max.floatValue"] floatValue];
    self.yBL = kLineChartHeight / maxY;
}


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

@end
1.gif
3、绘制类似QQ电话那个波形图
//
//  ViewController.m
//  CoreAnimation
//
//  Created by 意一yiyi on 2017/11/13.
//  Copyright © 2017年 意一yiyi. All rights reserved.
//

/**
 *  正弦函数往这一摆 : y = Asin(ωx+φ)+h
 
 *  A(振幅):决定峰值,也就是说靠它来决定正弦波的高度
 *  ω(频率):决定周期,最小正周期T=2π/|ω|,也就是说靠它来决定一个屏幕里能显示几个完整的正弦波
 *  φ(初相):决定起始点偏移X轴的位置,也就是说靠它来决定屏幕最左边起始点偏离X轴的距离
 *  h(偏移):决定波形偏移X轴的位置,也就是说靠它来决定整条正弦波偏离X轴的距离
 */

#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) CAShapeLayer *layer2;
@property (strong, nonatomic) CAShapeLayer *layer3;

@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;
    
    
    UIBezierPath *path2 = [[UIBezierPath alloc] init];
    [path2 moveToPoint:CGPointMake(0, self.waveAmplitude)];
    for (CGFloat x = 0.0; x < kScreenWidth; x ++) {
        
        CGFloat y = self.waveAmplitude * sinf(2.5 * M_PI * x / kScreenWidth + self.waveOffset * M_PI / kScreenWidth + M_PI / 4.0) + 3.0;
        [path2 addLineToPoint:CGPointMake(x, y)];
    }
    self.layer2.path = path2.CGPath;
    
    UIBezierPath *path3 = [[UIBezierPath alloc] init];
    [path3 moveToPoint:CGPointMake(0, self.waveAmplitude)];
    for (CGFloat x = 0.0; x < kScreenWidth; x ++) {
        
        CGFloat y = self.waveAmplitude * sinf(2 * M_PI * x / kScreenWidth + self.waveOffset * M_PI / kScreenWidth + M_PI / 2.0) + 6.0;
        [path3 addLineToPoint:CGPointMake(x, y)];
    }
    self.layer3.path = path3.CGPath;
}


#pragma mark - layoutUI

- (void)layoutUI {
    
    [self.view addSubview:self.contentView];
    [self.contentView.layer addSublayer:self.layer1];
    [self.contentView.layer addSublayer:self.layer2];
    [self.contentView.layer addSublayer:self.layer3];
}


#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;
}

- (CAShapeLayer *)layer2 {

    if (_layer2 == nil) {

        _layer2 = [CAShapeLayer layer];
        _layer2.strokeColor = [UIColor greenColor].CGColor;
        _layer2.fillColor = [UIColor clearColor].CGColor;
        _layer2.lineWidth =  5;
        _layer2.lineCap = kCALineCapRound;
        _layer2.lineJoin = kCALineJoinRound;
    }
    
    return _layer2;
}

- (CAShapeLayer *)layer3 {

    if (_layer3 == nil) {

        _layer3 = [CAShapeLayer layer];
        _layer3.strokeColor = [UIColor blueColor].CGColor;
        _layer3.fillColor = [UIColor clearColor].CGColor;
        _layer3.lineWidth =  5;
        _layer3.lineCap = kCALineCapRound;
        _layer3.lineJoin = kCALineJoinRound;
    }
    
    return _layer3;
}


#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

二、CAGradientLayer颜色渐变

CAGradientLayer是用来生成两种或更多种颜色渐变效果的,我们只需要将想要渐变的两个或多个颜色传给gradientLayer的colors数组属性,并设置一下gradientLayer颜色渐变的起点和终点确定下渐变色的朝向就可以了。

例子:

//
//  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 ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.navigationBar.hidden = YES;
    
    // 基础渐变
    CAGradientLayer *naviLayer = [[CAGradientLayer alloc] init];
    naviLayer.frame = CGRectMake(0, 0, kScreenWidth, 64);
    naviLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];
    naviLayer.startPoint = CGPointMake(0, 0);// 单位坐标
    naviLayer.endPoint = CGPointMake(1, 1);
    [self.view.layer addSublayer:naviLayer];
    
    
    // 多重渐变:gradientLayer的colors属性是可以传很多个颜色的,默认情况下这些颜色在空间上被均匀地渲染,但是我们也可以通过locations数组来自定义每个color的位置,但是要注意如果使用locations属性就要保证它的数量和colors数组一一对应
    CAGradientLayer *customLayer = [[CAGradientLayer alloc] init];
    customLayer.frame = CGRectMake(100, 100, 100, 100);
    customLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor orangeColor].CGColor, (__bridge id)[UIColor yellowColor].CGColor];
    customLayer.locations = @[@(0), @(0.25), @(0.5)];// 这样颜色渐变就被挤在左上角的一半了
    customLayer.startPoint = CGPointMake(0, 0);
    customLayer.endPoint = CGPointMake(1, 1);
    [self.view.layer addSublayer:customLayer];
}


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

@end
1.png

三、其它layer

除了上面举的两个例子之外,还有CATextLayer、CAReplicatorLayer等很多专用图层,我们可以根据自己的需求学习它们。

相关文章

  • 第五篇:CALayer能力之专用图层

    目录 一、CAShapeLayer结合UIBezierPath绘制任意形状的东西 二、CAGradientLaye...

  • 周老师的QuartzCore教程 - 专用图层(上)

    专用图层(上) CALayer的孩子们 专用图层是CALayer的子类,各自具有不同的功能,使用之后能够进一步扩展...

  • iOS-CALayer (三)

    上一篇 : iOS-CALayer (二) 前言:继续深入学习动画,主要从变换、专用图层出发。 一、变换 用于图层...

  • 从一个实际问题说说CAShapelayer

    Core Animation图层不仅仅只有CALayer这种简单的图片和颜色绘制的功能,还有一些专用图层,如:CA...

  • CoreAnimation之隐式动画

    CoreAnimation之CALayer基础CoreAnimation之变换CoreAnimation之常用图层...

  • iOS CALayer专用图层理解与使用方法一(CAShapeL

    概述 本专栏之前的文章讲述了CALayer的使用以及一些动画操作等,本篇文章主要对CALayer的一些专用图层CA...

  • CALayer图层类

    //CALayer图层类 //和UIView用法类似 //创建图层类对象 // CALayer *layer = ...

  • CALayer图层

    属性 contents该属性可通常指定一个CGImage,代表图层要显示的图片。 contentGravity该属...

  • 图层CALayer

    图层的树状结构 CoreAnimation其实是一个令人误解的命名。你可能认为它只是用来做动画的,但实际上它是从一...

  • CALayer图层

    一、 CALayer的简介 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、...

网友评论

      本文标题:第五篇:CALayer能力之专用图层

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