CAShapeLayer简介
- CAShapeLayer继承至CALayer,可以使用CALayer的所有属性值
- CAShapeLayer需要与贝塞尔曲线配合使用才有意义
- 使用CAShapeLayer与贝塞尔曲线可以实现不在view的drawRect方法中画出一些想要的图形
- CAShapeLayer属于CoreAnimation框架,其动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况
代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建shapeLayer
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.frame = (CGRect){CGPointMake(0, 0), CGSizeMake(200, 200)};
_shapeLayer.position = self.view.center;
_shapeLayer.path = [self getStar1BezierPath].CGPath;
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.strokeColor = [UIColor redColor].CGColor;
_shapeLayer.lineWidth = 2.f;
[self.view.layer addSublayer:_shapeLayer];
// 创建定时器
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f
target:self
selector:@selector(pathAnimation)
userInfo:nil
repeats:YES];
}
/**
* 执行path的动画
*/
- (void)pathAnimation {
static int i = 0;
if (i++ % 2 == 0) {
CABasicAnimation *circleAnim = [CABasicAnimation animationWithKeyPath:@"path"];
circleAnim.removedOnCompletion = NO;
circleAnim.duration = 1;
circleAnim.fromValue = (__bridge id)[self getStar1BezierPath].CGPath;
circleAnim.toValue = (__bridge id)[self getStar2BezierPath].CGPath;
_shapeLayer.path = [self getStar2BezierPath].CGPath;
[_shapeLayer addAnimation:circleAnim forKey:@"animateCirclePath"];
} else {
CABasicAnimation *circleAnim = [CABasicAnimation animationWithKeyPath:@"path"];
circleAnim.removedOnCompletion = NO;
circleAnim.duration = 1;
circleAnim.fromValue = (__bridge id)[self getStar2BezierPath].CGPath;
circleAnim.toValue = (__bridge id)[self getStar1BezierPath].CGPath;
_shapeLayer.path = [self getStar1BezierPath].CGPath;
[_shapeLayer addAnimation:circleAnim forKey:@"animateCirclePath"];
}
}
/**
* 贝塞尔曲线1
*
* @return 贝塞尔曲线
*/
-(UIBezierPath *)getStar1BezierPath {
//// Star Drawing
UIBezierPath* starPath = [UIBezierPath bezierPath];
[starPath moveToPoint: CGPointMake(22.5, 2.5)];
[starPath addLineToPoint: CGPointMake(28.32, 14.49)];
[starPath addLineToPoint: CGPointMake(41.52, 16.32)];
[starPath addLineToPoint: CGPointMake(31.92, 25.56)];
[starPath addLineToPoint: CGPointMake(34.26, 38.68)];
[starPath addLineToPoint: CGPointMake(22.5, 32.4)];
[starPath addLineToPoint: CGPointMake(10.74, 38.68)];
[starPath addLineToPoint: CGPointMake(13.08, 25.56)];
[starPath addLineToPoint: CGPointMake(3.48, 16.32)];
[starPath addLineToPoint: CGPointMake(16.68, 14.49)];
[starPath closePath];
return starPath;
}
/**
* 贝塞尔曲线2
*
* @return 贝塞尔曲线
*/
-(UIBezierPath *)getStar2BezierPath {
//// Star Drawing
UIBezierPath* starPath = [UIBezierPath bezierPath];
[starPath moveToPoint: CGPointMake(22.5, 2.5)];
[starPath addLineToPoint: CGPointMake(32.15, 9.21)];
[starPath addLineToPoint: CGPointMake(41.52, 16.32)];
[starPath addLineToPoint: CGPointMake(38.12, 27.57)];
[starPath addLineToPoint: CGPointMake(34.26, 38.68)];
[starPath addLineToPoint: CGPointMake(22.5, 38.92)];
[starPath addLineToPoint: CGPointMake(10.74, 38.68)];
[starPath addLineToPoint: CGPointMake(6.88, 27.57)];
[starPath addLineToPoint: CGPointMake(3.48, 16.32)];
[starPath addLineToPoint: CGPointMake(12.85, 9.21)];
[starPath closePath];
return starPath;
}
@end
贝塞尔曲线与CAShapeLayer的关系
- CAShapeLayer中有Shape这个单词,顾名思义,它需要一个形状才能生效
- 贝塞尔曲线可以创建基于矢量的路径
- 贝塞尔曲线给CAShapeLayer提供路径,CAShapeLayer在提供的路径中进行渲染,路径会闭环,所以路径绘制出了Shape
- 用于CAShapeLayer的贝塞尔曲线作为path,其path是一个首尾相接的闭环的曲线,即使该贝塞尔曲线不是一个闭环的曲线
代码
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 创建椭圆形贝塞尔曲线
UIBezierPath *oval = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 200, 100)];
// 创建矩形贝塞尔曲线
UIBezierPath *rect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 200, 100)];
// 创建圆形贝塞尔曲线
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
// 创建CAShapeLayer
CAShapeLayer *shape = [CAShapeLayer layer];
shape.frame = CGRectMake(0, 0, 200, 50);
shape.position = self.view.center;
// 显示CAShapeLayer的边界
shape.borderWidth = 1.f;
// 禁止内容显示超出CAShapeLayer的frame值
shape.masksToBounds = YES;
// 修改贝塞尔曲线的填充颜色
shape.fillColor = [UIColor redColor].CGColor;
// 建立贝塞尔曲线与CAShapeLayer之间的关联
shape.path = circle.CGPath;
// 添加并显示
[self.view.layer addSublayer:shape];
}
StrokeStart与StrokeEnd动画
进度条效果 - 利用GPU实现,不占内存
- 将ShapeLayer的fillColor设置成透明背景
- 设置线条的宽度(lineWidth)的值
- 设置线条的颜色
- 将strokeStart值设定成0,然后让strokeEnd的值变化触发隐式动画
- strokeStart的值一定小于strokeEnd
代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSTimer *timer; // 定时器
@property (nonatomic, strong) CAShapeLayer *shapeLayer; // 形状layer
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 设置背景色
self.view.backgroundColor = [UIColor colorWithRed:0.878 green:0.878 blue:0.878 alpha:1];
// 创建椭圆形贝塞尔曲线
UIBezierPath *oval = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
// 创建CAShapeLayer
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.frame = CGRectMake(0, 0, 100, 100);
_shapeLayer.position = self.view.center;
// 修改CAShapeLayer的线条相关值
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.strokeColor = [UIColor redColor].CGColor;
_shapeLayer.lineWidth = 2.f;
_shapeLayer.strokeStart = 0.f;
_shapeLayer.strokeEnd = 0.f;
// 建立贝塞尔曲线与CAShapeLayer之间的关联
_shapeLayer.path = oval.CGPath;
// 添加并显示
[self.view.layer addSublayer:_shapeLayer];
// 创建定时器
_timer = [NSTimer scheduledTimerWithTimeInterval:1.f
target:self
selector:@selector(animationEventTypeTwo)
userInfo:nil
repeats:YES];
}
/**
* 动画效果1
*/
- (void)animationEventTypeOne {
// 执行隐式动画
_shapeLayer.strokeEnd = arc4random() % 100 / 100.f;
}
/**
* 动画效果2
*/
- (void)animationEventTypeTwo {
CGFloat valueOne = arc4random() % 100 / 100.f;
CGFloat valueTwo = arc4random() % 100 / 100.f;
// 执行隐式动画
_shapeLayer.strokeStart = valueOne < valueTwo ? valueOne : valueTwo;
_shapeLayer.strokeEnd = valueOne > valueTwo ? valueOne : valueTwo;
}
用CAShapeLayer实现圆形进度条效果
- 确定需要设定的参数
- 实现细节
- 进行测试
代码
viewController.m
#import "ViewController.h"
#import "CircleView.h"
@interface ViewController ()
{
CircleView *circle;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
circle = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
circle.center = self.view.center;
circle.startValue = 0.5;
circle.lineWidth = 3.f;
circle.lineColor = [UIColor grayColor];
[self.view addSubview:circle];
[self performSelector:@selector(delayAnimation)
withObject:nil
afterDelay:3.f];
}
- (void)delayAnimation {
circle.value = 1.f;
}
circleView.h
#import <UIKit/UIKit.h>
@interface CircleView : UIView
@property (nonatomic, assign) CGFloat startValue; // 起始值(0~1)
@property (nonatomic, assign) CGFloat lineWidth; // 线宽(>0)
@property (nonatomic, strong) UIColor *lineColor; // 线条颜色
@property (nonatomic, assign) CGFloat value; // 变化的值
@end
circleView.m
#import "CircleView.h"
@interface CircleView ()
@property (nonatomic, strong) CAShapeLayer *shapeLayer;
@end
@implementation CircleView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// 创建出CAShapeLayer
_shapeLayer = [CAShapeLayer layer];
_shapeLayer.frame = self.bounds;
// 创建出贝塞尔曲线
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.bounds];
// 贝塞尔曲线与CAShapeLayer产生关联
_shapeLayer.path = path.CGPath;
// 基本配置
_shapeLayer.fillColor = [UIColor clearColor].CGColor;
_shapeLayer.lineWidth = 1.f;
_shapeLayer.strokeColor = [UIColor redColor].CGColor;
_shapeLayer.strokeEnd = 0.f;
// 添加到当前view
[self.layer addSublayer:_shapeLayer];
}
return self;
}
@synthesize startValue = _startValue;
- (void)setStartValue:(CGFloat)startValue {
_startValue = startValue;
_shapeLayer.strokeEnd = startValue;
}
- (CGFloat)startValue {
return _startValue;
}
@synthesize lineWidth = _lineWidth;
- (void)setLineWidth:(CGFloat)lineWidth {
_lineWidth = lineWidth;
_shapeLayer.lineWidth = lineWidth;
}
- (CGFloat)lineWidth {
return _lineWidth;
}
@synthesize lineColor = _lineColor;
- (void)setLineColor:(UIColor *)lineColor {
_lineColor = lineColor;
_shapeLayer.strokeColor = lineColor.CGColor;
}
- (UIColor *)lineColor {
return _lineColor;
}
@synthesize value = _value;
- (void)setValue:(CGFloat)value {
_value = value;
_shapeLayer.strokeEnd = value;
}
- (CGFloat)value {
return _value;
}
@end