統計圖之折線圖 - SJChartLine
效果圖
?
用法
// 初始化折線圖
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 . 自定義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>
畫陰影、圓環、點擊圖層等
因為畫完折線后點的坐標已經獲取到了,陰影以及其他視圖的添加就容易多了。有幾個注意的地方:
- 陰影的顯示范圍的起點為第一個折線點的最下邊,即我們數學坐標系的原點處,陰影的終點為,最后一個折線點的最下邊,即X軸上x坐標和最后一個折線點相同的點。經過點即為折線點
- 圓環就是自定義了一個視圖SJCircleView,添加在折線點出(也可以通過UIbezierPath畫)
- 點擊層視圖就是普通的視圖(UIView),添加了點擊手勢
- 彈出視圖為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