統計圖之折線圖 - SJChartLine

RalfEpstein 8年前發布 | 11K 次閱讀 C語言 iOS開發 移動開發

效果圖


?

用法

// 初始化折線圖
    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];</code></pre> 

知識點回顧

  1. UIBezierPath + CAShapeLayer 畫線
  2. UIBezierPath + CAGradientLayer 畫漸變色

畫圖步驟

畫坐標軸,刻度標簽以及網格線

  • 如圖:


?

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];
    

    } }</code></pre>

    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]; }</code></pre>

    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];
    

    } }</code></pre>

    畫折線、漸變陰影、以及點擊層視圖

    因為折線是畫在坐標系中,所以畫折線的視圖繼承自坐標系視圖 ,并設置坐標軸中的最大值屬性(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];
    

    }</code></pre>

    畫陰影、圓環、點擊圖層等

    因為畫完折線后點的坐標已經獲取到了,陰影以及其他視圖的添加就容易多了。有幾個注意的地方:

    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];
    

    } }</code></pre>

    • 點擊事件是點擊當前的點擊層,移除上一個彈出視圖,在該點擊層的折線點添加一個彈出視圖
    #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; }</code></pre>

    折線圖基本完成

    為折線圖添加 名稱 說明等信息

    自定義一個視圖,在視圖中添加 名稱 說明等視圖,在自定義的視圖中添加一個UIScrollView,ScrollView添加上折線圖

    ==注意:將折線圖添加在一個UIScrollView中,當設置X軸單位長度后,如果折線寬度大于ScrollView的寬度,便可以滑動。不設置X軸單位長度,折線圖會自適應ScrollView的寬度; 折線圖的高度智能通過設置ScrollView的高度來設置,即Y軸單位長度ScrollView自適應高度,垂直方向不可滑動==

     

     

    來自:http://www.cnblogs.com/jaesun/p/tong-ji-tu-zhi-zhe-xian-tu--SJChartLine.html

     

 本文由用戶 RalfEpstein 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!