本篇主要從以下幾個(gè)方面來寫的一點(diǎn)東西:
- 線段
- 曲線
- 動(dòng)畫
- 簡(jiǎn)單的柱狀圖
- 簡(jiǎn)單的折線圖
線段
- 單線段
兩點(diǎn)確定一條直線柄瑰,給貝塞爾曲線一個(gè)起始點(diǎn)moveToPoint
再添加一條線的終點(diǎn)addLineToPoint
,這樣就確定了一條直線。
- (void)drawLine {
UIView *view = [self.view viewWithTag:1024];
UILabel *label = [view viewWithTag:524];
label.text = @"直線";
CAShapeLayer *line = [CAShapeLayer layer];
line.lineWidth = 2;
line.strokeColor = [UIColor orangeColor].CGColor;
line.fillColor = nil;
[view.layer addSublayer:line];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100, 50)];
[bezierPath addLineToPoint:CGPointMake(200, 150)];
line.path = bezierPath.CGPath;
}
- 多線段
前面線段的終點(diǎn)是后面線段的起點(diǎn)。給一個(gè)起點(diǎn)moveToPoint
巾乳,然后想添加幾條線就給幾個(gè)線的終點(diǎn)addLineToPoint
衫哥。
- (void)drawDoubleLine {
UIView *view = [self.view viewWithTag:1025];
UILabel *label = [view viewWithTag:525];
label.text = @"折線";
CAShapeLayer *line = [CAShapeLayer layer];
line.lineWidth = 2;
line.strokeColor = [UIColor orangeColor].CGColor;
line.fillColor = nil;
[view.layer addSublayer:line];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(100, 50)];
[bezierPath addLineToPoint:CGPointMake(200, 150)];
[bezierPath addLineToPoint:CGPointMake(200, 100)];
[bezierPath addLineToPoint:CGPointMake(250, 150)];
line.path = bezierPath.CGPath;
}
- 閉合多邊形
也是多線段連起來的,只不過最后一條線的終點(diǎn)為第一條線段的起點(diǎn)堤如。
- (void)drawTriangle {
UIView *view = [self.view viewWithTag:1026];
UILabel *label = [view viewWithTag:526];
label.text = @"閉合多邊形";
CAShapeLayer *triangle = [CAShapeLayer layer];
triangle.lineWidth = 2;
triangle.strokeColor = [UIColor redColor].CGColor;
triangle.fillColor = [UIColor clearColor].CGColor;
[view.layer addSublayer:triangle];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(kDeviceWidth/2.0, 50)];
[bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0-100, 150)];
[bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0+100, 150)];
[bezierPath addLineToPoint:CGPointMake(kDeviceWidth/2.0, 50)];
triangle.path = bezierPath.CGPath;
}
-
線端點(diǎn)樣式
CAShapeLayer的lineCap
屬性決定線端點(diǎn)樣式,可選樣式kCALineCapButt(默認(rèn))
窒朋,kCALineCapRound(圓角)
搀罢,kCALineCapSquare(平角)
。默認(rèn)為kCALineCapButt
也是平角侥猩。
線端點(diǎn)樣式示例 -
線段拐點(diǎn)處樣式
CAShapeLayer的lineJoin
屬性決定線端點(diǎn)樣式榔至,可選樣式kCALineJoinMiter(尖角)
,kCALineJoinRound(圓角)
拭宁,kCALineJoinBevel(平角)
洛退。默認(rèn)為kCALineJoinMiter
。
拐角樣式示例 虛線
@property(nullable, copy) NSArray<NSNumber *> *lineDashPattern;
CAShapeLayer的lineDashPattern
屬性決定你畫出一條什么樣的虛線杰标,這個(gè)屬性返回一組NSNumber
類型的數(shù)組兵怯,其實(shí)就是實(shí)虛相交
來表示你的虛線,數(shù)組的長(zhǎng)度由你決定(當(dāng)然最好不要第一輪實(shí)虛相加超過線段長(zhǎng)度)腔剂。比如line.lineDashPattern = @[@10,@5,@2,@8];
就是表示每輪都為長(zhǎng)度為10的實(shí)線媒区,長(zhǎng)度為5的虛線,長(zhǎng)度為2的實(shí)線掸犬,長(zhǎng)度為8的虛線袜漩,循環(huán)直到線段結(jié)束。
曲線
-
二次貝塞爾曲線
二次貝塞爾曲線
二次貝塞爾曲線有一個(gè)控制點(diǎn)湾碎,控制點(diǎn)的位置決定了顯示一條怎樣的曲線宙攻。下面的例子,我把起點(diǎn)pA介褥、終點(diǎn)pB座掘、控制點(diǎn)pC 都畫出來方便觀察。
//篇幅限制 只貼主要代碼
//曲線
CAShapeLayer *layerOne = [CAShapeLayer layer];
layerOne.fillColor = [UIColor clearColor].CGColor;
layerOne.strokeColor = [UIColor blackColor].CGColor;
layerOne.strokeStart = 0;
layerOne.strokeEnd = 1;
[view.layer addSublayer:layerOne];
//路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:pA];
[path addQuadCurveToPoint:pB controlPoint:pC];
//關(guān)聯(lián)路徑
layerOne.path = path.CGPath;
-
三次貝塞爾曲線
三次貝塞爾曲線
三次貝塞爾曲線有兩個(gè)控制點(diǎn)柔滔,兩個(gè)控制點(diǎn)的位置決定了顯示一條怎樣的曲線溢陪。下面的例子,我把起點(diǎn)pA睛廊、終點(diǎn)pB形真、控制點(diǎn)pC、pD 都畫出來方便觀察超全。
//篇幅限制 只貼主要代碼
//曲線
CAShapeLayer *layerTwo = [CAShapeLayer layer];
layerTwo.fillColor = [UIColor clearColor].CGColor;
layerTwo.strokeColor = [UIColor blackColor].CGColor;
layerTwo.strokeStart = 0;
layerTwo.strokeEnd = 1;
[view.layer addSublayer:layerTwo];
//路徑
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:pA];
[path addCurveToPoint:pB controlPoint1:pC controlPoint2:pD];
//關(guān)聯(lián)路徑
layerTwo.path = path.CGPath;
- 圓角矩形
- (void)drawRectRound {
UIView *view = [self.view viewWithTag:1028];
UIBezierPath *rectRound = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(kDeviceWidth/2.0-100, 50, 200, 100) byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(20, 20)];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.strokeColor = [UIColor clearColor].CGColor;
layer.fillColor = [UIColor whiteColor].CGColor;
layer.path = rectRound.CGPath;
[view.layer addSublayer:layer];
}
- 虛線圓
如果是靜態(tài)的(無動(dòng)畫)咆霜,那么需要兩個(gè)貝塞爾圓環(huán)曲線表示內(nèi)圓和外圓邓馒,內(nèi)圓一周,外圓實(shí)時(shí)進(jìn)度裕便。
如果是動(dòng)態(tài)的(有動(dòng)畫)绒净,那么可以一個(gè)貝塞爾圓環(huán)曲線表示內(nèi)圓和外圓见咒,內(nèi)偿衰、外圓都一周,外圓添加動(dòng)畫改览,動(dòng)畫的toValue
標(biāo)志實(shí)時(shí)進(jìn)度下翎。
虛線圓
- (void)drawXuCircle {
UIView *view = [self.view viewWithTag:1029];
//底部虛圓
CAShapeLayer *xuCircle = [CAShapeLayer layer];
xuCircle.lineWidth = 10;
xuCircle.strokeColor = ColorWithHex(0xbebebe, 1).CGColor;
xuCircle.fillColor = nil;
xuCircle.lineJoin = kCALineJoinMiter;
xuCircle.lineDashPattern = @[@2,@3];
[view.layer addSublayer:xuCircle];
//外部虛圓
CAShapeLayer *circle = [CAShapeLayer layer];
circle.lineWidth = 10;
circle.strokeColor = ColorWithHex(0xa2d100, 1).CGColor;
circle.fillColor = nil;
circle.lineJoin = kCALineJoinMiter;
circle.lineDashPattern = @[@2,@3];
[view.layer addSublayer:circle];
UIBezierPath *xuBezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(kDeviceWidth/2.0, 100) radius:55 startAngle:-M_PI_2 endAngle:3*M_PI_2 clockwise:YES];
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(kDeviceWidth/2.0, 100) radius:55 startAngle:-M_PI_2 endAngle:M_PI_2 clockwise:YES];
xuCircle.path = xuBezierPath.CGPath;
circle.path = bezierPath.CGPath;
}
動(dòng)畫
現(xiàn)在我們來給一些圖形加上動(dòng)畫,使運(yùn)行起來更美觀宝当。
動(dòng)畫
- 主要寫了三類動(dòng)畫
1.最常用的普通動(dòng)畫
2.進(jìn)度條動(dòng)畫
3.其他屬性的動(dòng)畫(比如這里有重復(fù)次數(shù)和逆執(zhí)行)
//普通動(dòng)畫视事,strokeEnd
- (CABasicAnimation *)animComm {
if (_animComm == nil) {
_animComm = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_animComm.fromValue = @0.0;
_animComm.toValue = @1.0;
_animComm.duration = 2.0;
}
return _animComm;
}
//進(jìn)度條動(dòng)畫
- (CABasicAnimation *)animProgress {
if (_animProgress == nil) {
_animProgress = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_animProgress.fromValue = @0.0;
_animProgress.toValue = @0.7;
_animProgress.fillMode = kCAFillModeForwards;
_animProgress.removedOnCompletion = NO;
_animProgress.duration = 2.0;
}
return _animProgress;
}
//重復(fù)次數(shù),逆執(zhí)行試用
- (CABasicAnimation *)animRepeat {
if (_animRepeat == nil) {
_animRepeat = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_animRepeat.fromValue = @0.0;
_animRepeat.toValue = @1.0;
_animRepeat.duration = 2.0;
_animRepeat.autoreverses = YES;
_animRepeat.repeatCount = 10;
}
return _animRepeat;
}
簡(jiǎn)單的柱狀圖
這是個(gè)非常簡(jiǎn)單的柱狀圖庆揩,需要注意的是柱子的三個(gè)重要部分俐东,起點(diǎn)、終點(diǎn)订晌、柱寬虏辫。柱子由起點(diǎn)根據(jù)柱寬向左右兩邊擴(kuò)張,如下圖柱子的起點(diǎn)是位置2而不是位置1锈拨。
#import "SJBarChart.h"
static CGFloat const lineWidth = 1.0; //坐標(biāo)軸線寬
static CGFloat const distance = 20.0; //距屏幕邊距
static CGFloat const cornerW = 10.0f; //拐角長(zhǎng)度
static CGFloat const barWidth = 50.0f; //柱狀寬度
static CGFloat const space = 30.0f; //柱狀之間的間隔
static CGFloat const scale = 3.0f; //柱狀顯示高度計(jì)算比例 *scale
@interface SJBarChart ()
{
CGFloat selfW, selfH;
NSArray *source;
}
@property (nonatomic, strong) CAShapeLayer *xAxis;
@property (nonatomic, strong) CAShapeLayer *yAxis;
@property (nonatomic, strong) UIScrollView *barScrollView;
@property (nonatomic, strong) CABasicAnimation *animation;
@end
@implementation SJBarChart
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
//
selfW = frame.size.width;
selfH = frame.size.height;
self.backgroundColor = [UIColor lightGrayColor];
}
return self;
}
- (void)showBarChart:(NSArray *)sourceArray {
source = sourceArray;
[self addxyAxis];
[self addSubview:self.barScrollView];
_barScrollView.contentSize = CGSizeMake(sourceArray.count*(space+barWidth) + space, 0);
[sourceArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
CAShapeLayer *bar = [self drawBar:idx];
[_barScrollView.layer addSublayer:bar];
}];
}
//柱狀圖
- (CAShapeLayer *)drawBar:(NSInteger)index {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.fillColor = [UIColor clearColor].CGColor;
layer.strokeColor = [UIColor redColor].CGColor;
layer.lineWidth = barWidth;
//終點(diǎn)y
CGFloat y = _barScrollView.frame.size.height-60 - lineWidth/2.0 - ([[source objectAtIndex:index] floatValue] * scale);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake((space + barWidth)*index + (space+barWidth/2.0), _barScrollView.frame.size.height-60)];
[path addLineToPoint:CGPointMake((space + barWidth)*index + (space+barWidth/2.0), y)];
layer.path = path.CGPath;
[layer addAnimation:self.animation forKey:nil];
return layer;
}
//添加坐標(biāo)軸
- (void)addxyAxis {
self.xAxis = [self lineWithStartPoint:CGPointMake(distance, selfH-30) breakPoint:CGPointMake(kDeviceWidth-distance, selfH-30) endPoint:CGPointMake(kDeviceWidth-distance-cornerW, selfH-30-cornerW)];
self.yAxis = [self lineWithStartPoint:CGPointMake(distance+lineWidth/2.0, selfH-30) breakPoint:CGPointMake(distance, 30) endPoint:CGPointMake(distance+cornerW, 30+cornerW)];
[self.layer addSublayer:self.xAxis];
[self.layer addSublayer:self.yAxis];
}
//畫坐標(biāo)軸
- (CAShapeLayer *)lineWithStartPoint:(CGPoint)startPoint breakPoint:(CGPoint)breakPoint endPoint:(CGPoint)endPoint {
CAShapeLayer *line = [CAShapeLayer layer];
line.fillColor = [UIColor clearColor].CGColor;
line.strokeColor = [UIColor blackColor].CGColor;
line.lineWidth = 1.0;
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoint];
[linePath addLineToPoint:breakPoint];
[linePath addLineToPoint:endPoint];
line.path = linePath.CGPath;
[line addAnimation:self.animation forKey:@"xyLineStrokeEndAnimation"];
return line;
}
- (UIScrollView *)barScrollView {
if (_barScrollView == nil) {
_barScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(distance+lineWidth, 30, kDeviceWidth-distance*2-lineWidth-cornerW, selfH-60-lineWidth/2.0)];
_barScrollView.bounces = NO;
_barScrollView.showsHorizontalScrollIndicator = NO;
}
return _barScrollView;
}
- (CABasicAnimation *)animation {
if (_animation == nil) {
_animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_animation.fromValue = @0.0;
_animation.toValue = @1.0;
_animation.duration = 2.0;
_animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
}
return _animation;
}
@end
折線圖
柱狀圖是一條條單獨(dú)的線段砌庄,折線圖就是一條連起來的完整折線。
#import "SJLineChart.h"
static CGFloat const lineWidth = 1.0; //坐標(biāo)軸線寬
static CGFloat const distance = 20.0; //距屏幕邊距
static CGFloat const cornerW = 10.0f; //拐角長(zhǎng)度
static CGFloat const space = 50.0f; //柱狀之間的間隔
static CGFloat const scale = 3.0f; //直線顯示高度計(jì)算比例 *scale
static CGFloat const radius = 3.0f; //標(biāo)記每個(gè)點(diǎn)的小圓半徑
@interface SJLineChart ()
{
CGFloat selfW, selfH;
NSArray *source;
}
@property (nonatomic, strong) CAShapeLayer *xAxis;
@property (nonatomic, strong) CAShapeLayer *yAxis;
@property (nonatomic, strong) UIScrollView *lineScrollView;
@property (nonatomic, strong) CABasicAnimation *animation;
@end
@implementation SJLineChart
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
//
selfW = frame.size.width;
selfH = frame.size.height;
self.backgroundColor = [UIColor lightGrayColor];
}
return self;
}
- (void)showLineChart:(NSArray *)sourceArray {
source = sourceArray;
[self addxyAxis];
[self addSubview:self.lineScrollView];
_lineScrollView.contentSize = CGSizeMake(sourceArray.count*(space+1), 0);
[self drawLineChart:sourceArray];
[self drawPoint:sourceArray];
}
- (void)drawLineChart:(NSArray *)array {
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.strokeColor = [UIColor redColor].CGColor;
lineLayer.lineWidth = 2.0;
//軌跡
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(space, _lineScrollView.frame.size.height - 60 - lineWidth/2.0 - ([[array objectAtIndex:0] floatValue] * scale))];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//
if (idx > 0) {
CGFloat y = _lineScrollView.frame.size.height-60 - lineWidth/2.0 - ([obj floatValue] * scale);
[path addLineToPoint:CGPointMake(space*(idx+1), y)];
}
}];
lineLayer.path = path.CGPath;
[self.lineScrollView.layer addSublayer:lineLayer];
[lineLayer addAnimation:self.animation forKey:@"lineStrokeEndAnimation"];
}
//把點(diǎn)標(biāo)出來
- (void)drawPoint:(NSArray *)array {
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//
CGFloat y = _lineScrollView.frame.size.height - 60 - lineWidth/2.0 - [obj floatValue]*scale;
UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(space * (idx+1), y) radius:radius startAngle:0 endAngle:(M_PI)*2 clockwise:YES];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.fillColor = [UIColor orangeColor].CGColor;
circleLayer.strokeColor = [UIColor clearColor].CGColor;
circleLayer.path = circlePath.CGPath;
[_lineScrollView.layer addSublayer:circleLayer];
}];
}
//添加坐標(biāo)軸
- (void)addxyAxis {
self.xAxis = [self lineWithStartPoint:CGPointMake(distance, selfH-30) breakPoint:CGPointMake(kDeviceWidth-distance, selfH-30) endPoint:CGPointMake(kDeviceWidth-distance-cornerW, selfH-30-cornerW)];
self.yAxis = [self lineWithStartPoint:CGPointMake(distance+lineWidth/2.0, selfH-30) breakPoint:CGPointMake(distance, 30) endPoint:CGPointMake(distance+cornerW, 30+cornerW)];
[self.layer addSublayer:self.xAxis];
[self.layer addSublayer:self.yAxis];
}
//畫坐標(biāo)軸
- (CAShapeLayer *)lineWithStartPoint:(CGPoint)startPoint breakPoint:(CGPoint)breakPoint endPoint:(CGPoint)endPoint {
CAShapeLayer *line = [CAShapeLayer layer];
line.fillColor = [UIColor clearColor].CGColor;
line.strokeColor = [UIColor blackColor].CGColor;
line.lineWidth = 1.0;
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:startPoint];
[linePath addLineToPoint:breakPoint];
[linePath addLineToPoint:endPoint];
line.path = linePath.CGPath;
[line addAnimation:self.animation forKey:@"xyLineStrokeEndAnimation"];
return line;
}
- (UIScrollView *)lineScrollView {
if (_lineScrollView == nil) {
_lineScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(distance+lineWidth, 30, kDeviceWidth-distance*2-lineWidth, selfH-60-lineWidth/2.0)];
_lineScrollView.bounces = NO;
_lineScrollView.showsHorizontalScrollIndicator = NO;
}
return _lineScrollView;
}
- (CABasicAnimation *)animation {
if (_animation == nil) {
_animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
_animation.fromValue = @0.0;
_animation.toValue = @1.0;
_animation.duration = 2.0;
_animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
}
return _animation;
}
@end
結(jié)語(yǔ)
感謝閱讀全文的朋友虐译。
?demo地址 https://github.com/SPIREJ/SJCAShapeLayer
相關(guān)閱讀
上一篇:CAShapeLayer & UIBezierPath & CABasicAnimation