一.动态折线图效果图
1.首先绘制网格和坐标
CAReplicatorLayer
//
添加网格图层
//
网格列线
CAReplicatorLayer
*rowReplicatorLayer = [
CAReplicatorLayer
new
];
_xReplicatorLayer
= rowReplicatorLayer;
rowReplicatorLayer.
position
=
CGPointMake
(
0
,
0
);
CALayer
*rowBackLine = [
CALayer
new
];
_xBackLine
= rowBackLine;
[rowReplicatorLayer
addSublayer
:rowBackLine];
[mainView.
layer
addSublayer
:rowReplicatorLayer];
//
网格横线
CAReplicatorLayer
*columnReplicatorLayer = [
CAReplicatorLayer
new
];
_yReplicatorLayer
= columnReplicatorLayer;
columnReplicatorLayer.
position
=
CGPointMake
(
0
,
0
);
CALayer
*columnBackLine = [
CALayer
new
];
_yBackLine
= columnBackLine;
[columnReplicatorLayer
addSublayer
:columnBackLine];
[mainView.
layer
addSublayer
:columnReplicatorLayer];
//图层绘制动画
[
CATransaction
begin
];
[
CATransaction
setAnimationDuration
:
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
);
[
CATransaction
commit
];
2.绘制曲线和折点
//
曲线
_curveLineLayer
.
strokeColor
=
_curveLineColor
.
CGColor
;
_curveLineLayer
.
lineWidth
=
_curveLineWidth
;
CAShapeLayer
*curveLineLayer = [
CAShapeLayer
new
];
_curveLineLayer
= curveLineLayer;
curveLineLayer.
fillColor
=
nil
;
curveLineLayer.
lineJoin
=
kCALineJoinRound
;
[mainView.
layer
addSublayer
: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];
[
_pointData
addObject
:[
NSValue
valueWithCGPoint
:point]];
}
CGFloat
sum =
0
;
//
计算总长度
for
(
int
i =
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 = [
UIBezierPath
bezierPath
];
UIBezierPath
*backPath = [
UIBezierPath
bezierPath
];
CGPoint
firstPoint = [
_pointData
[
0
]
CGPointValue
];
CGPoint
lastPoint = [
_pointData
[
_pointData
.
count
-
1
]
CGPointValue
];
[path
moveToPoint
:firstPoint];
[backPath
moveToPoint
:
CGPointMake
(firstPoint.
x
,
_yBackLine
.
frame
.
size
.
height
+
_YunitLabelHeight
)];
for
(
NSValue
*pointValue
in
_pointData
) {
CGPoint
point = [pointValue
CGPointValue
];
if
(pointValue ==
_pointData
[
0
]) {
[backPath
addLineToPoint
:point];
continue
;
}
[backPath
addLineToPoint
:point];
[path
addLineToPoint
:point];
}
[backPath
addLineToPoint
:
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 = [
CAShapeLayer
new
];
_backLayer
= backLayer;
[mainView.
layer
addSublayer
:backLayer];
CAShapeLayer
*progressLayer = [
CAShapeLayer
layer
];
_progressLayer
= progressLayer;
[
_backLayer
setMask
: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
= [
UIColor
whiteColor
].
CGColor
;
//
将路径填充颜色设置为透明
_progressLayer
.
fillColor
= [
UIColor
redColor
].
CGColor
;
4.添加动画效果
4.1曲线绘制动画
CABasicAnimation
*pointAnim = [
CABasicAnimation
animationWithKeyPath
:
@"strokeEnd"
];
pointAnim.
fromValue
=
@0.0
;
pointAnim.
toValue
=
@1.0
;
pointAnim.
duration
=
_drawAnimationDuration
;
[
_curveLineLayer
addAnimation
:pointAnim
forKey
:
@"drawLine”
];
#warning 背景是默认全部填充的,无法像曲线一样移动,故采用
setMask:
方法,给它加上一个遮罩。利用遮罩的绘制动画,模拟出背景的动画
//
图层直线的轨迹
UIBezierPath
*path = [
UIBezierPath
bezierPath
];
#warning
起始点似乎有问题
[path
moveToPoint
:
CGPointMake
(-
_widthGrid
*
2
,
0
)];
[path
addLineToPoint
:
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 = [
CABasicAnimation
animationWithKeyPath
:
@"strokeEnd"
];
animate.
removedOnCompletion
=
NO
;
animate.
fillMode
=
kCAFillModeForwards
;
animate.
duration
= duration;
animate.
fromValue
=
@0.0
;
animate.
toValue
=
@(
progress
)
;
//
为图层添加动画
[
_progressLayer
addAnimation
:animate
forKey
:
@"drawProgress”
];
二.动态渐变色百分比移动图效果图
效果图
1.绘制渐变色曲线
CAGradientLayer
//
将渐变图层添加到
animationView
的图层上
[
self
.
animationView
.
layer
addSublayer
:
self
.
gradientLayer
];
[
self
.
gradientLayer
addSublayer
:
self
.
gradientColorLayer
];
_gradientLayer
= [
CALayer
layer
];
_gradientColorLayer
= [
CAGradientLayer
layer
];
_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
;
[
_gradientColorLayer
setColors
:[
NSArray
arrayWithObjects
:(
id
)
_startColor
.
CGColor
,(
id
)
_endColor
.
CGColor
,
nil
]];
//
渐变方向水平
[
_gradientColorLayer
setStartPoint
:
CGPointMake
(
0
,
1
)];
2.添加蒙版
CAShapeLayer
CGFloat
lineWidth =
progressHeight
;
_progressLayer
= [
CAShapeLayer
layer
];
_progressLayer
.
lineWidth
= lineWidth*
2
;
_progressLayer
.
lineCap
=
kCALineCapSquare
;
_progressLayer
.
strokeColor
= [
UIColor
whiteColor
].
CGColor
;
//
将路径填充颜色设置为透明
_progressLayer
.
fillColor
= [
UIColor
clearColor
].
CGColor
;
//
用
progressLayer
来截取渐变层
[
self
.
gradientLayer
setMask
:
self
.
progressLayer
];
3.绘制百分比向下箭头Label
/**
绘制向下的三角形(这里的路径超出了Label,当然我们只要不截取超出部分就行了,不用管原始Label中的文字是否居中)
*/
-(
void
)drawTriangle{
//
圆角矩形
UIBezierPath
*path = [
UIBezierPath
bezierPathWithRoundedRect
:
_labPercent
.
frame
cornerRadius
:
3
];
//
三角形
UIBezierPath
*trianglePath = [[
UIBezierPath
alloc
]
init
];
[trianglePath
moveToPoint
:
CGPointMake
(
_percentView
.
frame
.
size
.
width
/
2
-
2
,
_percentView
.
frame
.
size
.
height
-
5
)];;
[trianglePath
addLineToPoint
:
CGPointMake
(
_percentView
.
frame
.
size
.
width
/
2
,
_percentView
.
frame
.
size
.
height
)];
[trianglePath
addLineToPoint
:
CGPointMake
(
_percentView
.
frame
.
size
.
width
/
2
+
2
,
_percentView
.
frame
.
size
.
height
-
5
)];
//
扩展绘制路径
[path
appendPath
:trianglePath];
CAShapeLayer
*fillLayer = [
CAShapeLayer
layer
];
fillLayer.
path
= path.
CGPath
;
fillLayer.
fillColor
=
kBaseColor
.
CGColor
;
#warning
必须将他添加到最下一层,否则会遮挡
其他图层
// [_labPercent.layer addSublayer:fillLayer];
[
_labPercent
.
layer
insertSublayer
:fillLayer
atIndex
:
0
];
}
4.动画
//
图层直线的轨迹
UIBezierPath
*path = [
UIBezierPath
bezierPath
];
[path
moveToPoint
:
CGPointMake
(
0
,
0
)];
[path
addLineToPoint
:
CGPointMake
(
_animationView
.
frame
.
size
.
width
,
0
)];
_progressLayer
.
path
= path.
CGPath
;
self
.
progressLayer
.
strokeEnd
=
0.0
;
//
动画时间
CGFloat
duration =
_animationTime
;
//
进度程度
CGFloat
progress =
_percent
;
//strokeEnd
动画到某个点结束
CABasicAnimation
*animate = [
CABasicAnimation
animationWithKeyPath
:
@"strokeEnd"
];
animate.
removedOnCompletion
=
NO
;
animate.
fillMode
=
kCAFillModeForwards
;
animate.
duration
= duration;
animate.
fromValue
=
@0.0
;
#warning
存在偏差,目前不知道原因
animate.
toValue
=
@(
progress-
0.02)
;
//
为图层添加动画
[
self
.
progressLayer
addAnimation
:animate
forKey
:
@"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
(), ^{
[
UIView
animateWithDuration
:
_animationTime
animations
:^{
_percentView
.
center
=
CGPointMake
(
self
.
frame
.
size
.
width
*
_percent
,
_percentView
.
center
.
y
);
}];
});