项目需求需要根据数据画一个折线图,网上找了下没找到对应的第三方。只能默默的自己写个,废话就不多说了看下面效果图。
下面就主要说一下实现思路,文章末尾有源码。
坐标计算
由于屏幕的坐标是左上角为原点坐标(0,0),但是我们视觉看的时候原点应该是在左下角。对这个我们就需要反过来计算,还有一点就是比例的问题计算获得折线y轴坐标,x轴坐标是固定的等宽所以就不需要一一计算了。
对折线y轴坐标,我的处理方式:
//折线图高度 / (折线图最高点 + 最高点/5) = 比例
_maxPoint = maxCount + maxCount / 5;//最高点
_scale = SCR_W(234) / _maxPoint;//比例,SCR_W(234)为折线图的高度
折线图最高点 + 最高点/5这个主要是让折线不会达到最高点,相当于比最到点还高让图看起来比较美观。拿到比例后通过折线图高度 - 值x比例 = y轴坐标点
//起点
CGFloat startY = SCR_W(256) - [dataAry[idx] floatValue] * _scale;
//终点
CGFloat endY = SCR_W(256) - [dataAry[idx + 1] floatValue] * _scale;
这样算出了y轴坐标,下面就简单了。下面画线代码
- (void)drawLineWithBrokenLintData:(NSArray *)dataAry lineColor:(UIColor *)lineColor
{
if (dataAry.count == 0) {
return;
}
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.bounds;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 1;
shapeLayer.strokeColor = lineColor.CGColor;
UIBezierPath *path = [UIBezierPath bezierPath];
for (int idx = 0; idx < dataAry.count - 1; idx++) {
CGFloat startY = SCR_W(256) - [dataAry[idx] floatValue] * _scale;
CGFloat endY = SCR_W(256) - [dataAry[idx + 1] floatValue] * _scale;
[path moveToPoint:CGPointMake(_columnWidth * idx + SCR_W(10), startY)];
[path addLineToPoint:CGPointMake(_columnWidth * (idx + 1) + SCR_W(10), endY)];
shapeLayer.path = path.CGPath;
}
[self.layer insertSublayer:shapeLayer below:_currentView.layer];
/*
[dataAry enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//折线
if (idx == _column - 1) {
return ;
}
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.bounds;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineWidth = 1;
shapeLayer.strokeColor = lineColor.CGColor;
CGFloat startY = SCR_W(256) - [dataAry[idx] floatValue] * _scale;
CGFloat endY = SCR_W(256) - [dataAry[idx + 1] floatValue] * _scale;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(_columnWidth * idx + SCR_W(10), startY)];
[path addLineToPoint:CGPointMake(_columnWidth * (idx + 1) + SCR_W(10), endY)];
shapeLayer.path = path.CGPath;
[self.layer insertSublayer:shapeLayer below:_currentView.layer];
}];
*/
}
选中线,跟随手指移动
让选中的线跟随手指移动主要有两个点:
1、获取当前手指的位置坐标
2、判断当前手指的坐标是距离左边的分割线近还是距离右边的分割线近
获取当前手指触摸坐标
//触摸
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//获取触摸对象
UITouch * touch = touches.anyObject;
//返回触摸点在视图中的当前坐标
CGPoint point = [touch locationInView:[touch view]];
[UIView animateWithDuration:0.25 animations:^{
[_lineView setFrame:CGRectMake([self nearBy:point.x], 0, 1, SCR_W(280))];
_current = _current > _column -1 ? _column -1 : _current;
[_currentView setChartCurrentDetailWithTime:_timeAry[_current] income:_incomeAry[_current] refund:_refundAry[_current]];
}];
}
//拖动
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//获取触摸对象
UITouch * touch = touches.anyObject;
//返回触摸点在视图中的当前坐标
CGPoint point = [touch locationInView:[touch view]];
[UIView animateWithDuration:0.25 animations:^{
[_lineView setFrame:CGRectMake([self nearBy:point.x], 0, 1, SCR_W(280))];
_current = _current > _column -1 ? _column -1 : _current;
[_currentView setChartCurrentDetailWithTime:_timeAry[_current] income:_incomeAry[_current] refund:_refundAry[_current]];
}];
}
计算选中线距离左边or右边?
- (CGFloat)nearBy:(CGFloat)pointX
{
//超出边距
if (pointX > _columnWidth * (_column - 1) + SCR_W(10)) {
_current = _column - 1;
return _current * _columnWidth + SCR_W(10);
}
//当前位置在第几个
int tmpX = pointX /(_columnWidth);
//判断距离那边近
CGFloat currentX = pointX - tmpX * (_columnWidth);
_current = currentX - _columnWidth/2 > 0 ? tmpX + 1 : tmpX;
return _current * _columnWidth + SCR_W(10);
}
总体思路很简单,剩下的就是封装了,我把代码简单封装了下,上传到git源码上,有需要的可以去下载。