iOS绘图CALayer、UIBezierPath运用(边框、填充、复制、渐变)
一.动态折线图效果图
1.首先绘制网格和坐标CAReplicatorLayer
//添加网格图层
//网格列线
CAReplicatorLayer
*rowReplicatorLayer = [CAReplicatorLayernew];
_xReplicatorLayer
= rowReplicatorLayer;
rowReplicatorLayer.position=
CGPointMake(0,0);
CALayer
*rowBackLine = [CALayernew];
_xBackLine
= rowBackLine;
[rowReplicatorLayeraddSublayer:rowBackLine];
[mainView.layeraddSublayer:rowReplicatorLayer];
//网格横线
CAReplicatorLayer
*columnReplicatorLayer = [CAReplicatorLayernew];
_yReplicatorLayer
= columnReplicatorLayer;
columnReplicatorLayer.position=
CGPointMake(0,0);
CALayer
*columnBackLine = [CALayernew];
_yBackLine
= columnBackLine;
[columnReplicatorLayeraddSublayer:columnBackLine];
[mainView.layeraddSublayer:columnReplicatorLayer];
//图层绘制动画
[CATransactionbegin];
[CATransactionsetAnimationDuration:0];
CGFloat
rowSpacing =
_heightGrid;
CGFloat
columnSpacing =
_widthGrid;
//坐标轴这里我简单的使用label循环创建
_XLabelView.frame=
CGRectMake(_YLabelWidth-_widthGrid/2,_mainView.frame.size.height-_XLabelHeight,_mainView.frame.size.width-_YLabelWidth,_XLabelHeight);
_YLabelView.frame=
CGRectMake(0,0-_heightGrid/2+_YunitLabelHeight,_YLabelWidth,_mainView.frame.size.height-_XLabelHeight);
//图层复制(设置复制的数量)
_xReplicatorLayer.instanceCount=
_numberY+1;
_yReplicatorLayer.instanceCount=
_valueData.count;
_xReplicatorLayer.instanceTransform=
CATransform3DMakeTranslation(0,
rowSpacing +
_widthLine,0);
_yReplicatorLayer.instanceTransform=
CATransform3DMakeTranslation(columnSpacing +_widthLine,0,0);
//设置图层大小
_yReplicatorLayer.frame=
_xReplicatorLayer.frame=
CGRectMake(_YLabelWidth,_YunitLabelHeight,_mainView.frame.size.width-_YLabelWidth-_spaceWidth,_mainView.frame.size.height-_XLabelHeight-_YunitLabelHeight);
_yBackLine.frame=
CGRectMake(0,0,_widthLine,_yReplicatorLayer.frame.size.height);
_xBackLine.frame=
CGRectMake(0,0,_yReplicatorLayer.frame.size.width,_widthLine);
[CATransactioncommit];
2.绘制曲线和折点
//曲线
_curveLineLayer.strokeColor=
_curveLineColor.CGColor;
_curveLineLayer.lineWidth=
_curveLineWidth;
CAShapeLayer
*curveLineLayer = [CAShapeLayernew];
_curveLineLayer
= curveLineLayer;
curveLineLayer.fillColor=
nil;
curveLineLayer.lineJoin=
kCALineJoinRound;
[mainView.layeraddSublayer:curveLineLayer];
/**
将数值转换成坐标
*/
-(CGPoint)_changeValueToPoint:(NSDictionary*)data{
CGFloat
xValue = [data[JHChartViewX]floatValue];
CGFloat
yValue = [data[JHChartViewY]floatValue];
//x坐标等于value*宽度
//y坐标等于value/最大值*y高度
CGPoint
point =
CGPointMake(_YLabelWidth+
xValue *(_widthGrid+
_widthLine),_YunitLabelHeight+(1-yValue/_maxY)*_yBackLine.frame.size.height);
return
point;
}
/**
生成坐标点
*/
-(void)_creatPoint{
_pointData
=
@[].mutableCopy;
for
(NSDictionary*dict
in
_valueData) {
//转换成当前坐标点
CGPoint
point = [self_changeValueToPoint:dict];
[_pointDataaddObject:[NSValuevalueWithCGPoint:point]];
}
CGFloat
sum =
0;
//计算总长度
for
(inti =
0; i<_pointData.count-1;
i++) {
CGPoint
p1 = [_pointData[i]CGPointValue];
CGPoint
p2 = [_pointData[i+1]CGPointValue];
CGFloat
temp =
sqrt(pow((p1.x-p2.x),2)+pow((p1.y-p2.y),2));
sum += temp;
}
_sumLineWidth
= sum;
}
/**
生成路径
*/
-(void)_creatPath{
[self_creatPoint];
UIBezierPath
* path = [UIBezierPathbezierPath];
UIBezierPath
*backPath = [UIBezierPathbezierPath];
CGPoint
firstPoint = [_pointData[0]CGPointValue];
CGPoint
lastPoint = [_pointData[_pointData.count-
1]CGPointValue];
[pathmoveToPoint:firstPoint];
[backPathmoveToPoint:CGPointMake(firstPoint.x,_yBackLine.frame.size.height+_YunitLabelHeight)];
for
(NSValue*pointValuein
_pointData) {
CGPoint
point = [pointValue
CGPointValue];
if
(pointValue ==_pointData[0])
{
[backPathaddLineToPoint:point];
continue;
}
[backPathaddLineToPoint:point];
[pathaddLineToPoint:point];
}
[backPathaddLineToPoint:CGPointMake(lastPoint.x,_yBackLine.frame.size.height+_YunitLabelHeight)];
_path
= path;
//背景路径
_backPath
= backPath;
}
/**
绘制坐标点
@param point坐标点
@param index标记tag
*/
- (void)drawPoint:(CGPoint)point
withIndex:(NSInteger)index{
}
3.绘制背景
//封闭阴影
CAShapeLayer
* backLayer = [CAShapeLayernew];
_backLayer
= backLayer;
[mainView.layeraddSublayer:backLayer];
CAShapeLayer
*progressLayer = [CAShapeLayerlayer];
_progressLayer
= progressLayer;
[_backLayersetMask:progressLayer];
//背景
_backLayer.fillColor=
_fillLayerBackgroundColor.CGColor;
_backLayer.hidden=
_fillLayerHidden;
//背景路劲
_backLayer.path=
_backPath.CGPath;
//背景移动遮罩
CGFloat
lineWidth =
_yReplicatorLayer.frame.size.height+_YunitLabelHeight;
_progressLayer.lineWidth=
lineWidth*2;
_progressLayer.lineCap=
kCALineCapSquare;
_progressLayer.strokeColor=
[UIColorwhiteColor].CGColor;
//将路径填充颜色设置为透明
_progressLayer.fillColor=
[UIColorredColor].CGColor;
4.添加动画效果
4.1曲线绘制动画
CABasicAnimation*pointAnim = [CABasicAnimationanimationWithKeyPath:@"strokeEnd"];
pointAnim.fromValue=
@0.0;
pointAnim.toValue=
@1.0;
pointAnim.duration=
_drawAnimationDuration;
[_curveLineLayeraddAnimation:pointAnimforKey:@"drawLine”];
#warning 背景是默认全部填充的,无法像曲线一样移动,故采用setMask:方法,给它加上一个遮罩。利用遮罩的绘制动画,模拟出背景的动画
//图层直线的轨迹
UIBezierPath
*path = [UIBezierPathbezierPath];
#warning起始点似乎有问题
[pathmoveToPoint:CGPointMake(-_widthGrid*2,0)];
[pathaddLineToPoint:CGPointMake(_yReplicatorLayer.frame.size.width,0)];
_progressLayer.path=
path.CGPath;
_progressLayer.strokeEnd=
0.0;
//动画时间
CGFloat
duration =
_drawAnimationDuration*(_sumLineWidth/_yReplicatorLayer.frame.size.width);
//进度程度
CGFloat
progress =
1.0;
//strokeEnd
动画到某个点结束
CABasicAnimation
*animate = [CABasicAnimationanimationWithKeyPath:@"strokeEnd"];
animate.removedOnCompletion=
NO;
animate.fillMode=
kCAFillModeForwards;
animate.duration=
duration;
animate.fromValue=
@0.0;
animate.toValue=
@(progress);
//为图层添加动画
[_progressLayeraddAnimation:animateforKey:@"drawProgress”];
二.动态渐变色百分比移动图效果图
效果图
1.绘制渐变色曲线CAGradientLayer
//将渐变图层添加到animationView的图层上
[self.animationView.layeraddSublayer:self.gradientLayer];
[self.gradientLayeraddSublayer:self.gradientColorLayer];
_gradientLayer= [CALayerlayer];
_gradientColorLayer= [CAGradientLayerlayer];
_gradientLayer.frame=
CGRectMake(0,0,_animationView.frame.size.width,_animationView.frame.size.height);
_gradientColorLayer.frame=
CGRectMake(0,0,_animationView.frame.size.width,_animationView.frame.size.height);
_gradientColorLayer.cornerRadius=
_gradientLayer.frame.size.height/2;
[_gradientColorLayersetColors:[NSArrayarrayWithObjects:(id)_startColor.CGColor,(id)_endColor.CGColor,nil]];
//渐变方向水平
[_gradientColorLayersetStartPoint:CGPointMake(0,1)];
2.添加蒙版CAShapeLayer
CGFloatlineWidth =progressHeight;
_progressLayer
= [CAShapeLayerlayer];
_progressLayer.lineWidth=
lineWidth*2;
_progressLayer.lineCap=
kCALineCapSquare;
_progressLayer.strokeColor=
[UIColorwhiteColor].CGColor;
//将路径填充颜色设置为透明
_progressLayer.fillColor=
[UIColorclearColor].CGColor;
//用progressLayer来截取渐变层
[self.gradientLayersetMask:self.progressLayer];
3.绘制百分比向下箭头Label
/**
绘制向下的三角形(这里的路径超出了Label,当然我们只要不截取超出部分就行了,不用管原始Label中的文字是否居中)
*/
-(void)drawTriangle{
//圆角矩形
UIBezierPath
*path = [UIBezierPathbezierPathWithRoundedRect:_labPercent.framecornerRadius:3];
//三角形
UIBezierPath
*trianglePath = [[UIBezierPathalloc]init];
[trianglePathmoveToPoint:CGPointMake(_percentView.frame.size.width/2-2,_percentView.frame.size.height-5)];;
[trianglePathaddLineToPoint:CGPointMake(_percentView.frame.size.width/2,_percentView.frame.size.height)];
[trianglePathaddLineToPoint:CGPointMake(_percentView.frame.size.width/2+2,_percentView.frame.size.height-5)];
//扩展绘制路径
[pathappendPath:trianglePath];
CAShapeLayer
*fillLayer = [CAShapeLayerlayer];
fillLayer.path=
path.CGPath;
fillLayer.fillColor=
kBaseColor.CGColor;
#warning必须将他添加到最下一层,否则会遮挡其他图层
// [_labPercent.layer addSublayer:fillLayer];
[_labPercent.layerinsertSublayer:fillLayeratIndex:0];
}
4.动画
//图层直线的轨迹
UIBezierPath
*path = [UIBezierPathbezierPath];
[pathmoveToPoint:CGPointMake(0,0)];
[pathaddLineToPoint:CGPointMake(_animationView.frame.size.width,0)];
_progressLayer.path=
path.CGPath;
self.progressLayer.strokeEnd=
0.0;
//动画时间
CGFloat
duration =
_animationTime;
//进度程度
CGFloat
progress =
_percent;
//strokeEnd
动画到某个点结束
CABasicAnimation
*animate = [CABasicAnimationanimationWithKeyPath:@"strokeEnd"];
animate.removedOnCompletion=
NO;
animate.fillMode=
kCAFillModeForwards;
animate.duration=
duration;
animate.fromValue=
@0.0;
#warning存在偏差,目前不知道原因
animate.toValue=
@(progress-0.02);
//为图层添加动画
[self.progressLayeraddAnimation:animateforKey:@"anim1"];
//百分比标签动画(iOS
10在layoutSubView中失效???)
_percentView.center=
CGPointMake(0,_percentView.center.y);
#warning此处使用延迟,使动画生效了
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(0.01*
NSEC_PER_SEC)),dispatch_get_main_queue(),
^{
[UIViewanimateWithDuration:_animationTimeanimations:^{
_percentView.center=
CGPointMake(self.frame.size.width*_percent,_percentView.center.y);
}];
});