LMChartView-曲线,折线,柱状图

效果图.png

看完前任3,最大感悟: 初闻不识曲中意,再听已是曲中人

浮世三千,吾爱有三:日,月与卿。日为朝,月为暮,卿为朝朝暮暮.

大家都知道:男人越来越值钱,殊不知你爱的女人:越来越沧桑.,在这个不相信眼泪的城市,不停的打拼.你说都是为了她而奋斗,但是没有了她你就不奋斗了;你说再过几年就回去,再过几年,你难道不知道这几年是你们最为青春年少,芳华绝代的年纪,既然你做好回去的打算,而她也无心来这个钢铁混凝土的城市,何不加快你的脚步,在这美好的时间,彼此厮守.你说现在买房买车压力大,小城市没有高工资,没有工作机会.没错,但是你们的终极目标,是幸福生活.正如一个故事:http://video.tudou.com/v/XMTc4NTgyOTg0NA==.html.

悟已往之不谏,知来者之可追,实迷途其未远,觉今是而昨非


现在来说一下,这个Demo主要纪念我之前不怎么认真的地方,遇到图标都是找的第三方:
Charts
GRMustache
ZFChart
chartee
...

这个demo就根据项目需求,自定义写得,完全利用UIBezierPath画图:

pragma mark -坐标系的X,Y都是通过一条一条线画成

- (void)drawVerticalLine {
    //画竖线
    NSInteger spaceCount = self.shuXianAccount - 1;
    CGFloat space = HorizontalLineW / spaceCount;
    CGFloat startX;
    CGFloat startY;
    CGFloat endX;
    CGFloat endY;
    CGFloat lineW;
    UIColor *lineColor;
    
    for (NSInteger i = 1; i <= self.shuXianAccount; i ++) {
        if (i == 1) {
            lineW = Fix375(2);
            lineColor = Col_0x77cecd;
            startY = Fix375(7);
            endY = LineStartY + VerticalLineH + lineW / 2;
        } else {
            lineW = Fix375(1);
            lineColor = UIColorFromRGB(0xf5f5f5);
            startY = LineStartY;
            endY = LineStartY + VerticalLineH;
        }
        startX = LineStartX + (i - 1) * space - lineW / 2;
        endX = LineStartX + (i - 1) * space - lineW / 2;
        
        CGPoint startPoint = CGPointMake(startX, startY);
        CGPoint endPoint = CGPointMake(endX, endY);
        
        [self drawLineWithContext:context startPoint:startPoint endPoint:endPoint lineColor:lineColor lineWidth:lineW];
        
    }
    
    //X轴文字
    CGFloat textX;
    CGFloat textY;
    CGFloat textW;
    CGFloat textH;
    
    for (NSInteger i = 1; i <= self.xTitlesArr.count; i++) {
        UIColor *textColor;
        if (i == self.xTitlesArr.count) {
            textColor = Col_0x77cecd;
        } else {
            textColor = Col_0x999999;
        }
        
        NSString *xTitleStr = self.xTitlesArr[i - 1];
        NSDictionary *attDic = @{NSFontAttributeName : [UIFont systemFontOfSize:Fix375(9)],
                                 NSForegroundColorAttributeName:textColor};
        CGSize xTitleStrSize = [xTitleStr sizeWithAttributes:attDic];
        
        CGFloat textSpace =   HorizontalLineW / (self.xTitlesArr.count - 1);
        
        textX = LineStartX + (i - 1) * textSpace - xTitleStrSize.width / 2;
        textY = LineStartY + VerticalLineH + Fix375(5);
        textW = xTitleStrSize.width;
        textH = xTitleStrSize.height;
        
        CGRect xTitleStrRect = CGRectMake(textX ,textY , textW,textH);
        
        [xTitleStr drawInRect:xTitleStrRect withAttributes:attDic];
    }
    
    NSString *danwei;
    if (self.timeType == USkinTimeTypeDay) {
        danwei = @"(h)";
    } else {
        danwei = @"(d)";
    }
    
    NSDictionary *danweiAttDic = @{NSFontAttributeName : [UIFont systemFontOfSize:Fix375(9)],
                                   NSForegroundColorAttributeName:Col_0x999999};
    CGSize danweiStrSize = [danwei sizeWithAttributes:danweiAttDic];
    CGFloat textSpace =   HorizontalLineW / (self.xTitlesArr.count - 1);

    textX = LineStartX + (self.xTitlesArr.count - 1) * textSpace - danweiStrSize.width / 2 + Fix375(17);
    textY = LineStartY + VerticalLineH + Fix375(5);
    textW = danweiStrSize.width;
    textH = danweiStrSize.height;
    
    CGRect danweiRect = CGRectMake(textX, textY,textW, textH);
    [danwei drawInRect:danweiRect withAttributes:danweiAttDic];
    
  }

横线和竖线一样的原理

pragma mark - 画箭头

- (void)drawXArrowWithContext:(CGContextRef)context{
    UIBezierPath *xArrowPath = [self xArrrowPath];
    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetFillColorWithColor(context, Col_0x77cecd.CGColor);
    CGContextSetLineWidth(context, 1);
    CGContextAddPath(context, xArrowPath.CGPath);
    CGContextDrawPath(context, kCGPathFill);
}
- (void)drawYArrowWithContext:(CGContextRef)context {
    UIBezierPath *xArrowPath = [self yArrrowPath];
    CGContextSetAllowsAntialiasing(context, YES);
    CGContextSetFillColorWithColor(context, Col_0x77cecd.CGColor);
    CGContextSetLineWidth(context, 1);
    CGContextAddPath(context, xArrowPath.CGPath);
    CGContextDrawPath(context, kCGPathFill);
}
- (UIBezierPath *)xArrrowPath {
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint topPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) - Fix375(3), LineStartY + VerticalLineH - Fix375(4) + Fix375(1));
    CGPoint centerPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11), LineStartY + VerticalLineH + Fix375(1));
    CGPoint bottomPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) - Fix375(3), LineStartY + VerticalLineH + Fix375(4) + Fix375(1));
    CGPoint rightPoint = CGPointMake(LineStartX + HorizontalLineW + Fix375(11) + Fix375(10), LineStartY + VerticalLineH + Fix375(1));
    
    
    [path moveToPoint:topPoint];
    [path addLineToPoint:centerPoint];
    [path addLineToPoint:bottomPoint];
    [path addLineToPoint:rightPoint];
    [path addLineToPoint:topPoint];
    
    return path;
}
- (UIBezierPath *)yArrrowPath {
    UIBezierPath *path = [UIBezierPath bezierPath];
    CGPoint topPoint = CGPointMake(LineStartX - Fix375(1), 0);
    CGPoint leftPoint = CGPointMake(LineStartX - Fix375(1)  - Fix375(4), Fix375(10));
    CGPoint centerPoint = CGPointMake(LineStartX - Fix375(1) , Fix375(10) - Fix375(3));
    CGPoint rightPoint = CGPointMake(LineStartX - Fix375(1) + Fix375(4),Fix375(10));
    
    [path moveToPoint:topPoint];
    [path addLineToPoint:leftPoint];
    [path addLineToPoint:centerPoint];
    [path addLineToPoint:rightPoint];
    [path addLineToPoint:topPoint];
    
    return path;
}

无论画坐标还是三角形还有标度都是利用:UIBezierPath的基本用法:moveToPoint,addLineToPoint,drawInRect

接下来根据数据画图,还是基本用法

- (UIBezierPath *)linePathWihtYvalueArr:(NSArray *)yValueArr {
    UIBezierPath *path = [UIBezierPath bezierPath];
    for (NSInteger i = 0; i < yValueArr.count; i++) {
        CGPoint onePoint = [self linePointWithYvalue:yValueArr[i]  index:i];
        if (i == 0) {
            [path moveToPoint:onePoint];
        } else {
            [path addLineToPoint:onePoint];
        }
    }
    return path;
}

下面是填充,制作渐变效果

-(void)setGradientColor {
    UIBezierPath *linePath = [self fillPathWihtYvalueArr:self.yValuesArr];
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.fillColor = [UIColor whiteColor].CGColor;
    shapeLayer.strokeColor = Col_0x77cecd.CGColor;
    shapeLayer.path = linePath.CGPath;
    shapeLayer.lineWidth = 1.0f;
    
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(0, 0, self.width, self.height);
    [gradientLayer setColors:[NSArray arrayWithObjects:(id)Col_0x77cecd.CGColor,(id)Col_0xffffff.CGColor, nil]];
    gradientLayer.locations = @[@0, @1.0];
    gradientLayer.borderWidth  = 0.1;
    [gradientLayer setMask:shapeLayer];
    
    [self.layer addSublayer:gradientLayer];
}

中间写得还是比较仔细,尽量封装

外部使用

  NSArray *array = @[@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10",@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20",@"21",@"22",@"23"];

LMChartView1 *chart = [[LMChartView1 alloc] initWithXtitlesArr:array yTilesArr:nil timeType:USkinTimeTypeDay];
chart.backgroundColor = Col_0xffffff;
NSMutableArray *yValuesArr = @[@"0.1",@"0.11",@"0.2",@"0.23",@"0.54",@"0.5",@"0.46",@"0.37",@"0.68",@"0.9",@"0.51",@"0.11",@"0.12",@"0.13",@"0.14",@"0.15",@"0.16",@"0.17",@"0.18",@"0.19",@"0.32",@"0.21",@"0.12",@"0.02"].mutableCopy;
chart.frame = CGRectMake(0, 100, self.view.bounds.size.width,  Fix375(212));
[chart fillDataWithArr:yValuesArr];
[self.view addSubview:chart];

针对曲线,我写了一个分类,因为画曲线:addCurveToPoint , addQuadCurveToPoint

贝塞尔曲线的画法是由起点、终点、控制点三个参数来画的.

我很随意的定义了三个点,为了清楚显示它们的位置,我放了三个矩形在上面以便观察,然后调用 path.moveToPoint(startPoint) 让它移动到起始点,然后调用path.addQuadCurveToPoint(endPoint, controlPoint: controlPoint) 这个方法告诉它结束点和控制点,这样它就能画出一条有弧度的线条了,如果把fillColor设置一个颜色,那么它就会变成一个很丑的形状了,示例图如下

三点画曲线.png

控制点决定了它的曲率,曲线的顶点不等于控制点的位置,具体可以看一下贝塞尔曲线的定义,你还可以使用两个控制点来画,两个控制点可以使用方法 path.addCurveToPoint(endPoint, controlPoint1: controlPoint, controlPoint2: controlPoint2)来搞定

两个控制点来画.png

这个分类添加曲度和 三点控制,四点控制

-(void)addBezierThroughPoints:(NSArray *)pointArray
    {
        NSAssert(pointArray.count > 0, @"没有点");
        
        if (pointArray.count < 3) {
            switch (pointArray.count) {
                case 1:
                {
                    NSValue * point0Value = pointArray[0];
                    CGPoint point0 = [point0Value CGPointValue];
                    [self addLineToPoint:point0];
                }
                    break;
                case 2:
                {
                    NSValue * point0Value = pointArray[0];
                    CGPoint point0 = [point0Value CGPointValue];
                    NSValue * point1Value = pointArray[1];
                    CGPoint point1 = [point1Value CGPointValue];
                    [self addQuadCurveToPoint:point1 controlPoint:ControlPointForTheBezierCanThrough3Point(self.currentPoint, point0, point1)];
                }
                    break;
                default:
                    break;
            }
        }
        
        CGPoint previousPoint = CGPointZero;
        
        CGPoint previousCenterPoint = CGPointZero;
        CGPoint centerPoint = CGPointZero;
        CGFloat centerPointDistance = 0;
        
        CGFloat obliqueRatio = 0;
        CGFloat obliqueAngle = 0;
        
        CGPoint previousControlPoint1 = CGPointZero;
        CGPoint previousControlPoint2 = CGPointZero;
        CGPoint controlPoint1 = CGPointZero;
        
        previousPoint = self.currentPoint;
        
        for (int i = 0; i < pointArray.count; i++) {
            
            NSValue * pointIValue = pointArray[I];
            CGPoint pointI = [pointIValue CGPointValue];
            
            if (i > 0) {
                
                previousCenterPoint = CenterPointOf(self.currentPoint, previousPoint);
                centerPoint = CenterPointOf(previousPoint, pointI);
                
                centerPointDistance = DistanceBetweenPoint(previousCenterPoint, centerPoint);
                
                if (previousCenterPoint.x != centerPoint.x) {
                    
                    obliqueRatio = (centerPoint.y - previousCenterPoint.y) / (centerPoint.x - previousCenterPoint.x);
                    obliqueAngle = atan(obliqueRatio);
                }
                else {
                    obliqueAngle = M_PI_2;
                }
                
                previousControlPoint2 = CGPointMake(previousPoint.x - 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y - 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
                controlPoint1 = CGPointMake(previousPoint.x + 0.5 * self.contractionFactor * centerPointDistance * cos(obliqueAngle), previousPoint.y + 0.5 * self.contractionFactor * centerPointDistance * sin(obliqueAngle));
            }
            
            if (i == 1) {
                
                [self addQuadCurveToPoint:previousPoint controlPoint:previousControlPoint2];
            }
            else if (i > 1 && i < pointArray.count - 1) {
                
                [self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
            }
            else if (i == pointArray.count - 1) {
                
                [self addCurveToPoint:previousPoint controlPoint1:previousControlPoint1 controlPoint2:previousControlPoint2];
                [self addQuadCurveToPoint:pointI controlPoint:controlPoint1];
            }
            else {
                
            }
            
            previousControlPoint1 = controlPoint1;
            previousPoint = pointI;
        }
    }

感谢大家的支持,以后会尽量更多干货.
最后愿祝大家:有情人终成眷属,幸福永康

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容