登录 立即注册
金钱:

Code4App-iOS开发-iOS 开源代码库-iOS代码实例搜索-iOS特效示例-iOS代码例子下载-Code4App.com

折线图渐变背景色

[复制链接]
来自: Jaesun 分类: iOS精品源码 上传时间: 2016-9-19 13:25:51
Tag:图表 charts chart 折线图 数据显示

项目介绍:

项目需求一折线图,找了各种demo总有那么一点不符合要求,就自己动手自定义了一个,在这里分享了。

下面是具体用法,自定义的思路:

源码下载

效果图

<center>
统计图-折线图
</center>

用法

    // 初始化折线图
    SJLineChart *lineChart = [[SJLineChart alloc] initWithFrame:CGRectMake(10, 100, [UIScreen mainScreen].bounds.size.width - 20, 200)];
  
    // 设置折线图属性
    
    lineChart.title = @"折线图"; // 折线图名称
    lineChart.maxValue = 100;   // 最大值
    lineChart.yMarkTitles = @[@"20",@"40",@"60",@"80",@"100"]; // Y轴刻度标签
    
    [lineChart setXMarkTitlesAndValues:@[@{@"item":@"9月1日",@"count":@60},@{@"item":@"9月2日",@"count":@30},@{@"item":@"9月3日",@"count":@90},@{@"item":@"9月4日",@"count":@100},@{@"item":@"9月5日",@"count":@60},@{@"item":@"9月6日",@"count":@60},@{@"item":@"9月7日",@"count":@12}] titleKey:@"item" valueKey:@"count"]; // X轴刻度标签及相应的值

    // lineChart.xScaleMarkLEN = 60; // 可以不设,会根据视图的宽度自适应,设置后如果折线图的宽度大于视图宽度,折线图可以滑动
    
    //设置完数据等属性后绘图折线图
    [lineChart mapping];
    
    [self.view addSubview:lineChart];

知识点回顾

  1. UIBezierPath + CAShapeLayer 画线、画图

  2. UIBezierPath + CAGradientLayer 画渐背景

画图步骤

画坐标轴,刻度标签以及网格线

  • 如图:

<center>
坐标系图片

</center>

1 . 自定义SJAxisView(坐标轴视图)定义属性 xScaleMarkLEN(X轴单位长度) 和 yScaleMarkLEN (Y轴单位长度 starPoint(坐标轴起始点、xAxis_L(X轴长度)、yAxis_L(Y轴长度) 并宏定义标签的宽高 以及通过计算或赋值得到的属性值 然后通过 for 循环 添加X轴和Y轴上的刻度标签

#pragma mark  Y轴上的刻度标签
- (void)setupYMarkLabs {

    for (int i = 0; i < self.yMarkTitles.count; i ++) {
        
        UILabel *markLab = [[UILabel alloc] initWithFrame:CGRectMake(0, self.startPoint.y - YMARKLAB_HEIGHT / 2 + i * self.yScaleMarkLEN, YMARKLAB_WIDTH, YMARKLAB_HEIGHT)];
        markLab.textAlignment = NSTextAlignmentRight;
        markLab.font = [UIFont systemFontOfSize:12.0];
        markLab.text = [NSString stringWithFormat:@"%@", self.yMarkTitles[self.yMarkTitles.count - 1 - i]];
        [self addSubview:markLab];
    }
}

#pragma mark  X轴上的刻度标签
- (void)setupXMarkLabs {

    for (int i = 0;i < self.xMarkTitles.count; i ++) {
        UILabel *markLab = [[UILabel alloc] initWithFrame:CGRectMake(self.startPoint.x - XMARKLAB_WIDTH / 2 + i * self.xScaleMarkLEN, self.yAxis_L + self.startPoint.y + YMARKLAB_HEIGHT / 2, XMARKLAB_WIDTH, XMARKLAB_HEIGHT)];
        markLab.textAlignment = NSTextAlignmentCenter;
        markLab.font = [UIFont systemFontOfSize:11.0];
        markLab.text = self.xMarkTitles[i];
        [self addSubview:markLab];
    }
}

2 . 画X轴 、Y轴, 确定X轴Y轴的起点和终点后,通过UIBezierPath + CAShapeLayer画线

#pragma mark  Y轴
- (void)drawYAxsiLine {
    UIBezierPath *yAxisPath = [[UIBezierPath alloc] init];
    [yAxisPath moveToPoint:CGPointMake(self.startPoint.x, self.startPoint.y + self.yAxis_L)];
    [yAxisPath addLineToPoint:CGPointMake(self.startPoint.x, self.startPoint.y)];
    
    CAShapeLayer *yAxisLayer = [CAShapeLayer layer];
    [yAxisLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:1.5], nil]];    // 设置线为虚线
    yAxisLayer.lineWidth = 0.5;
    yAxisLayer.strokeColor = [UIColor redColor].CGColor;
    yAxisLayer.path = yAxisPath.CGPath;
    [self.layer addSublayer:yAxisLayer];
}

#pragma mark  X轴
- (void)drawXAxsiLine {
    UIBezierPath *xAxisPath = [[UIBezierPath alloc] init];
    [xAxisPath moveToPoint:CGPointMake(self.startPoint.x, self.yAxis_L + self.startPoint.y)];
    [xAxisPath addLineToPoint:CGPointMake(self.xAxis_L + self.startPoint.x, self.yAxis_L + self.startPoint.y)];
    
    CAShapeLayer *xAxisLayer = [CAShapeLayer layer];
    [xAxisLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:1.5], nil]];
    xAxisLayer.lineWidth = 0.5;
    xAxisLayer.strokeColor = [UIColor redColor].CGColor;
    xAxisLayer.path = xAxisPath.CGPath;
    [self.layer addSublayer:xAxisLayer];
}

3 . 网格线 (同 坐标轴)

与Y轴平行的网格线的X坐标 第一条线的x坐标即为 坐标系中startPoint.x,后面的各线的x坐标加上相应倍数的X轴单位长度(xScaleMarkLEN)。

与Y轴平行的网格线Y坐标 第一条的y坐标为 startPoint.y ,后边的各线的y坐标加上相应倍数的y轴单位长度

#pragma mark  与 Y轴 平行的网格线
- (void)drawYGridline {
    for (int i = 0; i < self.xMarkTitles.count - 1; i ++) {
        
        CGFloat curMark_X = self.startPoint.x + self.xScaleMarkLEN * (i + 1);
        
        UIBezierPath *yAxisPath = [[UIBezierPath alloc] init];
        [yAxisPath moveToPoint:CGPointMake(curMark_X, self.yAxis_L + self.startPoint.y)];
        [yAxisPath addLineToPoint:CGPointMake(curMark_X, self.startPoint.y)];
        
        CAShapeLayer *yAxisLayer = [CAShapeLayer layer];
        [yAxisLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:1.5], nil]]; // 设置线为虚线
        yAxisLayer.lineWidth = 0.5;
        yAxisLayer.strokeColor = [UIColor blackColor].CGColor;
        yAxisLayer.path = yAxisPath.CGPath;
        [self.layer addSublayer:yAxisLayer];
    }
}

#pragma mark  与 X轴 平行的网格线
- (void)drawXGridline {
    for (int i = 0; i < self.yMarkTitles.count - 1; i ++) {
        
        CGFloat curMark_Y = self.yScaleMarkLEN * i;
        
        UIBezierPath *xAxisPath = [[UIBezierPath alloc] init];
        [xAxisPath moveToPoint:CGPointMake(self.startPoint.x, curMark_Y + self.startPoint.y)];
        [xAxisPath addLineToPoint:CGPointMake(self.startPoint.x + self.xAxis_L, curMark_Y + self.startPoint.y)];
        
        CAShapeLayer *xAxisLayer = [CAShapeLayer layer];
        [xAxisLayer setLineDashPattern:[NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:1.5], nil]];
        xAxisLayer.lineWidth = 0.5;
        xAxisLayer.strokeColor = [UIColor blackColor].CGColor;
        xAxisLayer.path = xAxisPath.CGPath;
        [self.layer addSublayer:xAxisLayer];
    }
}

画折线、渐变阴影、以及点击层视图

因为折线是画在坐标系中,所以画折线的视图继承自坐标系视图 ,并设置坐标轴中的最大值属性(maxValue)和 折线点值得数组(valueArray)

==折线图的重点是确定折线上各点的位置坐标==

画折线

折线点X坐标 折线点所在,Y网格线的 x坐标(确定折线的各点坐标第一个点的x坐标即为 坐标系中startPoint.x,后面的各点加上相应倍数的X轴单位长度(xScaleMarkLEN)。)
折线点Y坐标 假设折线点的值为value ,那么 percent = value / maxVlue 即为,折线点应该在其所在Y网格线的位置的百分比,因为iOS屏中坐标是以左上为起点,所以点的Y坐标为:起点的Y坐标(starPoint.y) + (1 - percent) * Y轴坐标轴长度

#pragma mark 画折线图
- (void)drawChartLine
    {
        UIBezierPath *pAxisPath = [[UIBezierPath alloc] init];
        
        for (int i = 0; i < self.valueArray.count; i ++) {
            
            CGFloat point_X = self.xScaleMarkLEN * i + self.startPoint.x;
            
            CGFloat value = [self.valueArray[i] floatValue];
            CGFloat percent = value / self.maxValue;
            CGFloat point_Y = self.yAxis_L * (1 - percent) + self.startPoint.y;
            
            CGPoint point = CGPointMake(point_X, point_Y);
            
            // 记录各点的坐标方便后边添加渐变阴影 和 点击层视图 等
            [pointArray addObject:[NSValue valueWithCGPoint:point]];
            
            if (i == 0) {
                [pAxisPath moveToPoint:point];
            }
            else {
                [pAxisPath addLineToPoint:point];
            }
        }

        CAShapeLayer *pAxisLayer = [CAShapeLayer layer];
        pAxisLayer.lineWidth = 1;
        pAxisLayer.strokeColor = [UIColor orangeColor].CGColor;
        pAxisLayer.fillColor = [UIColor clearColor].CGColor;
        pAxisLayer.path = pAxisPath.CGPath;
        [self.layer addSublayer:pAxisLayer];
}

画阴影、圆环、点击图层等

因为画完折线后点的坐标已经获取到了,阴影以及其他视图的添加就容易多了。有几个注意的地方:

  1. 阴影的显示范围的起点为第一个折线点的最下边,即我们数学坐标系的原点处,阴影的终点为,最后一个折线点的最下边,即X轴上x坐标和最后一个折线点相同的点。经过点即为折线点

  2. 圆环就是自定义了一个视图SJCircleView,添加在折线点出(也可以通过UIbezierPath画)

  3. 点击层视图就是普通的视图(UIView),添加了点击手势

  4. 弹出视图为UIButton,设置了特殊背景图片

#pragma mark 渐变阴影
- (void)drawGradient {
        
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
    gradientLayer.colors = @[(__bridge id)[UIColor colorWithRed:250/255.0 green:170/255.0 blue:10/255.0 alpha:0.8].CGColor,(__bridge id)[UIColor colorWithWhite:1 alpha:0.1].CGColor];

    gradientLayer.locations=@[@0.0,@1.0];
    gradientLayer.startPoint = CGPointMake(0.0,0.0);
    gradientLayer.endPoint = CGPointMake(0.0,1);
    
    UIBezierPath *gradientPath = [[UIBezierPath alloc] init];
    [gradientPath moveToPoint:CGPointMake(self.startPoint.x, self.yAxis_L + self.startPoint.y)];
    
    for (int i = 0; i < pointArray.count; i ++) {
        [gradientPath addLineToPoint:[pointArray[i] CGPointValue]];
    }
    
    CGPoint endPoint = [[pointArray lastObject] CGPointValue];
    endPoint = CGPointMake(endPoint.x + self.startPoint.x, self.yAxis_L + self.startPoint.y);
    [gradientPath addLineToPoint:endPoint];
    CAShapeLayer *arc = [CAShapeLayer layer];
    arc.path = gradientPath.CGPath;
    gradientLayer.mask = arc;
    [self.layer addSublayer:gradientLayer];

}

#pragma mark 折线上的圆环
- (void)setupCircleViews {
    
    for (int i = 0; i < pointArray.count; i ++) {
        
        SJCircleView *circleView = [[SJCircleView alloc] initWithCenter:[pointArray[i] CGPointValue] radius:4];
        circleView.tag = i + BASE_TAG_CIRCLEVIEW;
        circleView.borderColor = [UIColor orangeColor];
        circleView.borderWidth = 1.0;
        [self addSubview:circleView];
    }
}

#pragma mark 覆盖一层点击图层
- (void)setupCoverViews {

    for (int i = 0; i < pointArray.count; i ++) {
        
        UIView *coverView = [[UIView alloc] init];
        coverView.tag = BASE_TAG_COVERVIEW + i;
                
        if (i == 0) {
            
            coverView.frame = CGRectMake(self.startPoint.x, self.startPoint.y, self.xScaleMarkLEN  / 2, self.yAxis_L);
            [self addSubview:coverView];
        }
        else if (i == pointArray.count - 1 && pointArray.count == self.xMarkTitles.count) {
            CGPoint point = [pointArray[i] CGPointValue];
            coverView.frame = CGRectMake(point.x - self.xScaleMarkLEN / 2, self.startPoint.y, self.xScaleMarkLEN  / 2, self.yAxis_L);
            [self addSubview:coverView];
        }
        else {
            CGPoint point = [pointArray[i] CGPointValue];
            coverView.frame = CGRectMake(point.x - self.xScaleMarkLEN / 2, self.startPoint.y, self.xScaleMarkLEN, self.yAxis_L);
            [self addSubview:coverView];
        }
        UITapGestureRecognizer *gesutre = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gesutreAction:)];
        [coverView addGestureRecognizer:gesutre];
    }
}
  • 点击事件是点击当前的点击层,移除上一个弹出视图,在该点击层的折线点添加一个弹出视图

#pragma mark- 点击层视图的点击事件
- (void)gesutreAction:(UITapGestureRecognizer *)sender {
    
    NSInteger index = sender.view.tag - BASE_TAG_COVERVIEW;
    
    if (lastSelectedIndex != -1) {
        
        SJCircleView *lastCircleView = (SJCircleView *)[self viewWithTag:lastSelectedIndex + BASE_TAG_CIRCLEVIEW];
        lastCircleView.borderWidth = 1;
        
        UIButton *lastPopBtn = (UIButton *)[self viewWithTag:lastSelectedIndex + BASE_TAG_POPBTN];
        [lastPopBtn removeFromSuperview];
    }
    
    SJCircleView *circleView = (SJCircleView *)[self viewWithTag:index + BASE_TAG_CIRCLEVIEW];
    circleView.borderWidth = 2;
    
    CGPoint point = [pointArray[index] CGPointValue];
    
    UIButton *popBtn = [UIButton buttonWithType:(UIButtonTypeCustom)];
    popBtn.tag = index + BASE_TAG_POPBTN;
    popBtn.frame = CGRectMake(point.x - 10, point.y - 20, 20, 15);
    
    [popBtn setBackgroundImage:[UIImage imageNamed:@"btg_pop_bg.png"] forState:UIControlStateNormal];
    
    [popBtn setTitleEdgeInsets:UIEdgeInsetsMake(- 3, 0, 0, 0)];
    popBtn.titleLabel.font = [UIFont systemFontOfSize:10];
    [popBtn setTitle:[NSString stringWithFormat:@"%@",self.valueArray[index]] forState:(UIControlStateNormal)];
    
    [self addSubview:popBtn];

    lastSelectedIndex = index;
}

折线图基本完成

为折线图添加 名称 说明等信息

自定义一个视图,在视图中添加 名称 说明等视图,在自定义的视图中添加一个UIScrollView,ScrollView添加上折线图

==注意:将折线图添加在一个UIScrollView中,当设置X轴单位长度后,如果折线宽度大于ScrollView的宽度,便可以滑动。不设置X轴单位长度,折线图会自适应ScrollView的宽度; 折线图的高度智能通过设置ScrollView的高度来设置,即Y轴单位长度ScrollView自适应高度,垂直方向不可滑动==


简单记录自定义折线图的思路和关键点,具体见源码

相关源码推荐:

我来说两句
*滑动验证:
所有评论(16)
Jaesun 2016-9-19 13:44:43
冒号全变成 表情符号了,哈哈
回复
李世铿 2016-9-19 17:29:14
Jaesun 发表于 2016-9-19 13:44
冒号全变成 表情符号了,哈哈

发帖把禁用表情勾选上就可以了
回复
Jaesun 2016-9-19 20:38:21
哦哦,下次知道了,谢谢
回复
用户39048 2016-9-20 11:36:55
我现在要画个类似的图不过数据一直是动的,线也已直随着时间走,思路怎么走呢?
回复
Jaesun 2016-9-20 12:27:41
用户39048 发表于 2016-9-20 11:36
我现在要画个类似的图不过数据一直是动的,线也已直随着时间走,思路怎么走呢? ...

根据你的需求,我的初步思路是这样的:
数据改变一次,就重新绘制一次图;线随时间走,可以把放在scrollView中,改变contentoffsize试一试。
回复
Jaesun 2016-9-20 12:29:19
Jaesun 发表于 2016-9-20 12:27
根据你的需求,我的初步思路是这样的:
数据改变一次,就重新绘制一次图;线随时间走,可以把放在scrollV ...

你可以去找一个股票K线图的Demo看看,应该有你这样的需求。
回复
用户39048 2016-9-20 12:42:11
Jaesun 发表于 2016-9-20 12:29
你可以去找一个股票K线图的Demo看看,应该有你这样的需求。

好的!我试试。
回复
lsjzhuce 2016-10-14 10:32:00
学习学习!
回复
Frank_春 2016-10-15 00:25:38
这个如果放在tableView里面,没刷新一次就会有重复的绘图,覆盖在上面,这个是怎么回事啊
回复
12下一页
4445 6 0
联系我们
首页/微信公众账号投稿

帖子代码编辑/版权问题

QQ:435399051,742864542

如何获得代码达人称号?

代码贡献英雄榜
用户名 下载数
通过邮件订阅最新 Code4App 信息
上一条 /4 下一条
联系我们
关闭
合作电话:
13802416937
Email:
435399051@qq.com
商务市场合作/投稿
问题反馈及帮助
联系我们

广告投放| Github|申请友链|手机版|Code4App ( 粤ICP备15117877号-1 )

快速回复 返回顶部 返回列表