zoukankan      html  css  js  c++  java
  • iOS 柱状图的定制

    最近因项目需求,要使用柱状图,第三方的东西固然很好,但是我还是想要写一个自己的柱状图,因为更加符合我们项目的需求,日后维护起来也会方便许多。

    写了一个自定义视图,可以自定义众多属性,关键是使用起来方便,最少四行就可以了。

    废话不多说,上代码:

    WQLChartView.h文件:

     1 typedef NS_ENUM(NSInteger ,ChartViewType) {
     2     ChartViewTypeColumn,
     3     ChartViewTypePoint
     4 };
     5 
     6 @interface WQLChartView : UIView
     7 /**
     8  *  图表的类型
     9  */
    10 @property (nonatomic,assign) ChartViewType type;
    11 /**
    12  *  单列的宽度
    13  */
    14 @property (nonatomic,assign) CGFloat singleRowWidth;
    15 /**
    16  *  每个柱子之间的间距
    17  */
    18 @property (nonatomic,assign) CGFloat singleChartSpace;
    19 /**
    20  *  x的值 数组
    21  */
    22 @property (nonatomic,strong) NSArray *xValueArray;
    23 /**
    24  *  y的值 数组
    25  */
    26 @property (nonatomic,strong) NSArray *yValueArray;
    27 /**
    28  *  x轴的右侧标题(比如:时间)
    29  */
    30 @property (nonatomic,copy) NSString *xAxleTitle;
    31 /**
    32  *  y轴的顶部标题 (比如:万件)
    33  */
    34 @property (nonatomic,copy) NSString *yAxleTitle;
    35 /**
    36  *  y轴上有几个点
    37  */
    38 @property (nonatomic,assign) NSInteger yAxlePointNumber;
    39 /**
    40  *  y轴的坐标点 数组
    41  */
    42 @property (nonatomic,strong) NSArray *yPointArray;
    43 /**
    44  *  柱子的颜色
    45  */
    46 @property (nonatomic,strong) UIColor *columnColor;
    47 /**
    48  *  是否显示线条
    49  */
    50 @property (nonatomic,assign) BOOL showLine;
    51 /**
    52  *  连线的颜色
    53  */
    54 @property (nonatomic,strong) UIColor *lineColor;
    55 /**
    56  *  连线的宽度(粗细)
    57  */
    58 @property (nonatomic,assign) CGFloat lineWidth;
    59 /**
    60  *  点的颜色(只有是point类型才有效)
    61  */
    62 @property (nonatomic,strong) UIColor *pointColor;
    63 /**
    64  *  点的宽度(只有point类型才有效)
    65  */
    66 @property (nonatomic,assign) CGFloat pointWidth;
    67 /**
    68  *  连接是否使用平滑的曲线
    69  */
    70 @property (nonatomic,assign) BOOL lineIsCurve;
    71 /**
    72  *  是否隐藏数值 默认不隐藏
    73  */
    74 @property (nonatomic,assign) BOOL isHideNumber;
    75 /**
    76  *  数值的颜色
    77  */
    78 @property (nonatomic,strong) UIColor *colorOfNumber;
    79 /**
    80  *  数值的字号
    81  */
    82 @property (nonatomic,assign) NSInteger fontSizeOfNumber;
    83 
    84 //在superView上展示视图 必须调用 参数配置完毕后调用
    85 - (void)showChartInView:(UIView*)superView;
    86 
    87 //更新视图
    88 - (void)updateView;

    WQLChartView.m文件 核心代码:

      1 #pragma  mark 添加柱状图
      2 - (void)loadColumnShapeWithSingleWidth:(CGFloat)singleW withSpace:(CGFloat)singleSpace
      3 {
      4     
      5     CGFloat viewHeight = self.bounds.size.height;
      6     //如果改动了 则把之前的显示部分移除掉
      7     if (isChange) {
      8         [self deletePointLayer];
      9     }
     10     
     11     //如果之前 添加了 则把之前的删了
     12     if (columnShapeLayerArray.count > 0) {
     13         for (CAShapeLayer *layer in columnShapeLayerArray) {
     14             [layer removeFromSuperlayer];
     15         }
     16     }
     17     
     18     //把之前添加的线 移除掉
     19     if (lineArray.count > 0) {
     20         for (CAShapeLayer *lineLayer in lineArray) {
     21             [lineLayer removeFromSuperlayer];
     22         }
     23     }
     24     
     25     columnShapeLayerArray = [NSMutableArray array];
     26     lineArray = [NSMutableArray array];
     27     
     28     UIBezierPath *column = [UIBezierPath bezierPath];
     29     
     30     CGFloat xPosition = 0;
     31     if (!topPointYArray) {
     32         topPointYArray = [NSMutableArray array];
     33     }else{
     34         [topPointYArray removeAllObjects];
     35     }
     36     
     37     for (int i = 0; i<xCount; i++) {
     38         CAShapeLayer *columnLayer = [CAShapeLayer layer];
     39         //当前的y值
     40         NSString *value = _yValueArray[i];
     41         CGFloat yValue = [value floatValue];
     42         //y值 占坐标最大值的比率
     43         CGFloat rate = yValue/yMax;
     44         //单个柱子的高度
     45         CGFloat singleHeight = (viewHeight-titleWidth-yArrowHeight)*rate;
     46         
     47         NSString *topPointY = [NSString stringWithFormat:@"%f",viewHeight-titleWidth-singleHeight];
     48         [topPointYArray addObject:topPointY];
     49         
     50         //x轴 为柱子左下侧的点 y为坐标轴
     51         [column moveToPoint:CGPointMake(xPosition+singleSpace+titleWidth, viewHeight-titleWidth)];
     52         //柱子的左侧垂直线
     53         [column addLineToPoint:CGPointMake(xPosition+singleSpace+titleWidth, viewHeight-titleWidth-singleHeight)];
     54         //柱子的顶部水平线
     55         [column addLineToPoint:CGPointMake(xPosition+singleSpace+titleWidth+singleW, viewHeight-titleWidth-singleHeight)];
     56         //柱子的右侧垂直线
     57         [column addLineToPoint:CGPointMake(xPosition+singleSpace+titleWidth+singleW, viewHeight-titleWidth)];
     58         
     59         if (self.showLine) {
     60             
     61             UIBezierPath *line = [UIBezierPath bezierPath];
     62             
     63             CAShapeLayer *lineLayer = [CAShapeLayer layer];
     64             
     65             if (i>0) {
     66                 //上一个点的y值
     67                 NSString *lastValue = _yValueArray[i-1];
     68                 CGFloat lastYValue = [lastValue floatValue];
     69                 CGFloat lastRate = lastYValue/yMax;
     70                 //取到上一个点的高度
     71                 CGFloat lastSingleHeight = (viewHeight-titleWidth-yArrowHeight)*lastRate;
     72                 
     73                 //移到柱子的顶部中点
     74                 [line moveToPoint:CGPointMake(xPosition+singleSpace+titleWidth+singleW/2, viewHeight-titleWidth-singleHeight)];
     75                 //向上一个点 添加连线
     76                 if (self.lineIsCurve) {
     77                     //曲线连接
     78                     //上一个点的x坐标
     79                     CGFloat lastPointX = xPosition+singleSpace+titleWidth+singleW/2-(singleW+singleSpace);
     80                     //上一个点的y坐标
     81                     CGFloat lastPointY = viewHeight - titleWidth-lastSingleHeight;
     82                     //该柱子顶部中点的x坐标
     83                     CGFloat pointX = xPosition+singleSpace+titleWidth+singleW/2;
     84                     //该柱子顶部中点的y坐标
     85                     CGFloat pointY = viewHeight-titleWidth-singleHeight;
     86                     //添加曲线 两个控制点 x为两个点的中间点 y为首末点的y坐标 为了实现平滑连接
     87                     [line addCurveToPoint:CGPointMake(lastPointX, lastPointY) controlPoint1:CGPointMake((pointX+lastPointX)/2, pointY) controlPoint2:CGPointMake((pointX+lastPointX)/2, lastPointY)];
     88                 }else{
     89                     //直线连接
     90                     [line addLineToPoint:CGPointMake(xPosition+singleSpace+titleWidth+singleW/2-(singleW+singleSpace), viewHeight-titleWidth-lastSingleHeight)];
     91                 }
     92                 
     93             }
     94             lineLayer.path = line.CGPath;
     95             //线条宽度
     96             lineLayer.lineWidth = self.lineWidth>0?self.lineWidth:2;
     97             if (!self.lineColor) {
     98                 self.lineColor = [UIColor redColor];
     99             }
    100             //线条颜色
    101             lineLayer.strokeColor = self.lineColor.CGColor;
    102             lineLayer.fillColor = [UIColor clearColor].CGColor;
    103             [lineArray addObject:lineLayer];
    104             
    105         }
    106         
    107         columnLayer.path = column.CGPath;
    108         if (!self.columnColor) {
    109             self.columnColor = [UIColor orangeColor];
    110         }
    111         
    112         columnLayer.fillColor = CGColorCreateCopyWithAlpha(self.columnColor.CGColor, 1.0);
    113         columnLayer.strokeStart = 0;
    114         columnLayer.strokeEnd = 1.0;
    115         [columnShapeLayerArray addObject:columnLayer];
    116         
    117         [self.layer addSublayer:columnLayer];
    118         
    119         for (CAShapeLayer *lineLayer in lineArray) {
    120             [self.layer addSublayer:lineLayer];
    121         }
    122         
    123         xPosition += (singleW+singleSpace);
    124         
    125     }
    126 }
    127 #pragma  mark  添加 点视图
    128 - (void)loadPointInChartView
    129 {
    130     if (isChange) {
    131         [self deleteColumnLayer];
    132     }
    133     
    134     UIBezierPath *circle = [UIBezierPath bezierPath];
    135     
    136     CGFloat viewHeight = self.bounds.size.height;
    137     
    138     //如果之前 添加了 则把之前的删了
    139     if (pointShapeLayerArray.count > 0) {
    140         for (CAShapeLayer *layer in pointShapeLayerArray) {
    141             [layer removeFromSuperlayer];
    142         }
    143     }
    144     
    145     //把之前添加的线条删除了
    146     if (lineArray.count > 0) {
    147         for (CAShapeLayer *lineLayer in lineArray) {
    148             [lineLayer removeFromSuperlayer];
    149         }
    150     }
    151     
    152     pointShapeLayerArray = [NSMutableArray array];
    153     lineArray = [NSMutableArray array];
    154     
    155     if (!topPointYArray) {
    156         topPointYArray = [NSMutableArray array];
    157     }else{
    158         [topPointYArray removeAllObjects];
    159     }
    160     //顶部中点连线
    161     NSInteger count = topCenterPointXArray.count;
    162     if (count >0) {
    163         
    164         for (int i = 0; i<topCenterPointXArray.count; i++) {
    165             
    166             CAShapeLayer *circleLayer = [CAShapeLayer layer];
    167             
    168             NSString *topPoint = topCenterPointXArray[i];
    169             CGFloat pointX = [topPoint floatValue];
    170             //y值
    171             CGFloat yValues = [self.yValueArray[i] floatValue];
    172             //y坐标又是不一样了 y值越大,柱子越高,坐标其实是越小
    173             CGFloat pointY = (1-(yValues/yMax))*(viewHeight-yArrowHeight-titleWidth)+yArrowHeight;
    174             
    175             [topPointYArray addObject:[NSString stringWithFormat:@"%f",pointY]];
    176             
    177             [circle moveToPoint:CGPointMake(pointX, pointY)];
    178             
    179             CGFloat radius = self.pointWidth >0 ?self.pointWidth:5.0;
    180             //标示点 为圆
    181             [circle  addArcWithCenter:CGPointMake(pointX, pointY) radius:radius startAngle:0 endAngle:M_PI*2 clockwise:YES];
    182             circleLayer.path = circle.CGPath;
    183             if (!self.pointColor) {
    184                 self.pointColor = [UIColor orangeColor];
    185             }
    186             //圆点的填充色
    187             circleLayer.fillColor = CGColorCreateCopyWithAlpha(self.pointColor.CGColor, 1.0);
    188             circleLayer.strokeStart = 0;
    189             circleLayer.strokeEnd = 1.0;
    190             circleLayer.lineWidth = 1;
    191             [pointShapeLayerArray addObject:circleLayer];
    192             
    193             if (self.showLine) {
    194                 
    195                 UIBezierPath *line = [UIBezierPath bezierPath];
    196                 
    197                 CAShapeLayer *lineLayer = [CAShapeLayer layer];
    198                 
    199                 if (i>0) {
    200                     NSString *lastXValue = topCenterPointXArray[i-1];
    201                     CGFloat lastPointX = [lastXValue floatValue];
    202                     
    203                     NSString *lastValue = _yValueArray[i-1];
    204                     CGFloat lastYValue = [lastValue floatValue];
    205                     //y坐标又是不一样了 y值越大,柱子越高,坐标其实是越小
    206                     CGFloat lastPointY = (1-(lastYValue/yMax))*(viewHeight-yArrowHeight-titleWidth)+yArrowHeight;
    207                     
    208                     //移到中点
    209                     [line moveToPoint:CGPointMake(pointX,pointY)];
    210                     
    211                     if (self.lineIsCurve) {
    212                         //曲线连接 两个控制点 x为两个点的中间点 y为首末点的y坐标 为了实现平滑连接
    213                         [line addCurveToPoint:CGPointMake(lastPointX, lastPointY) controlPoint1:CGPointMake((pointX+lastPointX)/2, pointY) controlPoint2:CGPointMake((pointX+lastPointX)/2, lastPointY)];
    214                     }else{
    215                         //向上一个点 添加连线
    216                         [line addLineToPoint:CGPointMake(lastPointX,lastPointY)];
    217                     }
    218                     
    219                 }
    220                 lineLayer.path = line.CGPath;
    221                 lineLayer.lineWidth = self.lineWidth>0?self.lineWidth:2;
    222                 if (!self.lineColor) {
    223                     self.lineColor = [UIColor blackColor];
    224                 }
    225                 lineLayer.strokeColor = self.lineColor.CGColor;
    226                 lineLayer.fillColor = [UIColor clearColor].CGColor;
    227                 [lineArray addObject:lineLayer];
    228                 
    229             }
    230             
    231         }
    232     }
    233     
    234     for (CAShapeLayer *layer  in pointShapeLayerArray) {
    235         [self.layer addSublayer:layer];
    236     }
    237     
    238     for (CAShapeLayer *lineLayer in lineArray) {
    239         [self.layer addSublayer:lineLayer];
    240     }
    241     
    242 }

    总计八百多行,我就不一一贴出了。上述两个方法是核心的方法:

    添加柱状图时,要注意坐标系的转换,因为我们布局的时候,坐标原点为视图的左上角,我们需要展示的柱状图的原点则是视图的左下角。

    我们先获取到每一个y的值,然后计算出了每一个值占最大值的比率,然后用这个比率乘以y轴的总高度,则为对应的柱状图高度。

    我们获取到每一个x值之后,要进行计算,比较用户设置的柱子的宽度*柱子的个数 与 初始设定的视图的宽度的大小,如果前者较大,则需要微调,以展示全部的柱子,后者较大,则不做处理。

    对于线条的处理,直线图无需多余的处理,对于曲线图,使用的是有两个控制点的贝塞尔曲线,这两个控制点:
    点A的x为始末点的中点的x,A的y为起点的y值。B的x为始末点的中点的x,y为末点的y值。这样就可以实现曲线平滑连接始末点了。

    接下来看一下该怎么使用:

     1 - (void)loadChartView
     2 {
     3     chartView = [[WQLChartView alloc]initWithFrame:CGRectMake(10, 100,chartWidth, chartHeight)];
     4     chartView.singleRowWidth = 50;//可注释掉
     5     chartView.columnColor = [UIColor lightGrayColor];//可注释掉
     6     chartView.pointColor = [UIColor orangeColor];//可注释掉
     7     chartView.xAxleTitle = @"";//可注释掉
     8     chartView.yAxleTitle = @"";//可注释掉
     9     chartView.type = ChartViewTypeColumn;//可注释掉
    10     chartView.showLine = YES;//可注释掉
    11     chartView.lineColor = [UIColor blueColor];//可注释掉
    12     chartView.lineIsCurve = YES;//可注释掉
    13     chartView.colorOfNumber = [UIColor redColor];//可注释掉
    14     chartView.yPointArray = @[@"20",@"40",@"60",@"80",@"100"];//可注释掉
    15     chartView.xValueArray = xValuesArray;
    16     chartView.yValueArray = yValuesArray;
    17     [chartView showChartInView:self.view];
    18 
    19 }

    最简单的就是全部非必要属性使用默认的,仅需4行即可加载柱状图。

    回到要点上,功能才是王道:

    添加柱子:

    切换类型:

    由头文件的属性可知,以下属性都可以修改:

    图表的类型:分为柱状的和点状的
    单列的宽度:柱状图时有效,默认25
    每个柱子之间的间距:柱状图时有效
    x的值数组:必须赋值的属性,为X轴上的点
    y的值数组:必须赋值的属性,为对应的x的y值
    x轴的右侧标题:可设置属性,为x轴的单位
    y轴的顶部标题:可设置属性,为y轴的单位
    y轴上的点的个数:可设置属性,默认为5个点,即y轴被分为5等份
    y轴的坐标点数组:y轴上的刻度点
    柱子的颜色:可设置属性,默认为橘黄色
    是否显示线条:可设置属性,默认不显示线条
    连线的颜色:可设置属性,默认为红色
    连线的宽度:可设置属性,默认为2
    点的颜色:点状图时有效,默认橙色
    点的宽度:点状图时有效,标示的点的半径
    是否使用平滑的曲线:连线的形状,默认使用直线连接
    是否隐藏数值:不显示数字,默认为NO,不隐藏
    数值的颜色:显示的数值的文本颜色,默认为黑色
    数值的字号:数值的文本字号,默认为14号

    需要额外提一点的是:用户的柱子宽度一定,当数量比较多时,原来的给定的尺寸不足以显示的时候,我将柱子的宽度缩小了以实现刚好填充的效果。

    如有不足之处还请各位园友指导~一起学习一起进步!

    完整的代码在这里:                 ChartView 1                            

     

  • 相关阅读:
    第二节:Java环境变量配置
    第一节:VS充当IIS的配置步骤(VS2017和VS2019)
    .Net进阶系列(21)-跨域请求
    02-Unity深入浅出(二)
    第十五节:Expression表达式目录树(与委托的区别、自行拼接、总结几类实例间的拷贝)
    01-Unity深入浅出(一)
    第十二节:SQLServer存储过程详解及EFCore调用
    Android 组件系列-----Activity的传值和回传值
    Access大数据高效分页语句
    C#清除HTML样式
  • 原文地址:https://www.cnblogs.com/520myp1314/p/5311668.html
Copyright © 2011-2022 走看看