CombinedChart
最近公司項目中要做一個報表功能,需要用到圖表,于是就使用了 Charts 這個框架,這個框架可以說是圖表中用的最多的框架了,由于
Charts
只有Swift
版本,公司項目是用OC
寫的,所以先整理一下OC
的使用方法,Swift
等以后項目轉了在整理。
1. 創建 CombinedChartView 并設置屬性
下面列出一些常用的設置屬性,可以根據需求自行設置。
@interface CombinedChartViewController()
@property (nonatomic, strong) CombinedChartView *combinedChartView; // 柱狀折線組合圖
@end
@implementation CombinedChartViewController
- (void)viewDidLoad {
[super viewDidLoad];
/* 設置圖表的屬性 */
_combinedChartView = [[CombinedChartView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 350)];
_combinedChartView.drawOrder = @[@(CombinedChartDrawOrderBar), @(CombinedChartDrawOrderLine)]; // 繪制順序(折線圖在柱狀圖上面)
_combinedChartView.noDataText = @"暫無數據"; // 無數據時顯示的文字
_combinedChartView.descriptionText = @""; // 描述文字
_combinedChartView.legend.enabled = NO; // 隱藏圖例
_combinedChartView.pinchZoomEnabled = NO; // 觸控放大
_combinedChartView.doubleTapToZoomEnabled = NO; // 雙擊放大
_combinedChartView.scaleXEnabled = NO; // X 軸縮放
_combinedChartView.scaleYEnabled = NO; // Y 軸縮放
_combinedChartView.scaleEnabled = NO; // 縮放
_combinedChartView.highlightPerTapEnabled = NO; // 單擊高亮
_combinedChartView.highlightPerDragEnabled = NO; // 拖拽高亮
_combinedChartView.dragEnabled = YES; // 拖拽圖表
_combinedChartView.dragDecelerationEnabled = YES; // 拖拽后是否有慣性效果
_combinedChartView.dragDecelerationFrictionCoef = 0.5; // 拖拽后慣性效果的摩擦系數(0~1),數值越小,慣性越不明顯
[self.view addSubview:_combinedChartView];
/* 設置 X 軸顯示的值的屬性 */
ChartXAxis *xAxis = _combinedChartView.xAxis;
xAxis.labelPosition = XAxisLabelPositionBottom; // 顯示位置
xAxis.drawGridLinesEnabled = NO; // 網格繪制
xAxis.axisLineColor = [UIColor lightGrayColor]; // X 軸顏色
xAxis.axisLineWidth = 0.5f; // X 軸線寬
xAxis.labelFont = [UIFont systemFontOfSize:10]; // 字號
xAxis.labelTextColor = [UIColor lightGrayColor]; // 顏色
xAxis.labelRotationAngle = 30; // 文字傾斜角度
/* 設置左側 Y 軸顯示的值的屬性 */
ChartYAxis *leftAxis = _combinedChartView.leftAxis;
leftAxis.labelPosition = YAxisLabelPositionOutsideChart; // 顯示位置
leftAxis.drawGridLinesEnabled = YES; // 網格繪制
leftAxis.gridColor = [UIColor lightGrayColor]; // 網格顏色
leftAxis.gridLineWidth = 0.5f; // 網格線寬
leftAxis.drawAxisLineEnabled = NO; // 是否顯示軸線
leftAxis.labelFont = [UIFont systemFontOfSize:10]; // 字號
leftAxis.labelTextColor = [UIColor lightGrayColor]; // 顏色
leftAxis.axisMinimum = 0; // 最小值
leftAxis.axisMaximum = 500; // 最大值(不設置會根據數據自動設置)
[leftAxis setLabelCount:6 force:YES]; // Y 軸段數(會自動分成對應段數)
/* 設置右側 Y 軸顯示的值的屬性 */
ChartYAxis *rightAxis = _combinedChartView.rightAxis;
rightAxis.labelPosition = YAxisLabelPositionOutsideChart; // 顯示位置
rightAxis.drawGridLinesEnabled = NO; // 網格繪制
rightAxis.drawAxisLineEnabled = NO; // 是否顯示軸線
rightAxis.labelFont = [UIFont systemFontOfSize:10]; // 字號
rightAxis.labelTextColor = [UIColor lightGrayColor]; // 顏色
rightAxis.axisMinimum = 0; // 最小值
rightAxis.axisMaximum = 100; // 最大值(不設置會根據數據自動設置)
[rightAxis setLabelCount:6 force:YES]; // Y 軸段數(會自動分成對應段數)
}
@end
2. 設置柱狀圖屬性
下面將設置柱狀圖屬性與數據進行了封裝。
/**
柱狀圖的數據
@param bar1Values 第一段的數據
@param bar2Values 第二段的數據
@return 柱狀圖的數據
*/
- (BarChartData *)getBarData:(NSArray *)bar1Values bar2Values:(NSArray *)bar2Values {
NSMutableArray *barEntries = [NSMutableArray array];
if (bar1Values.count == bar2Values.count) {
for (int i=0; i<bar1Values.count; i++) {
BarChartDataEntry *barEntry = [[BarChartDataEntry alloc] initWithX:i yValues:@[bar1Values[i], bar2Values[i]]];
[barEntries addObject:barEntry];
}
}
BarChartDataSet *dataSet = [[BarChartDataSet alloc] initWithValues:barEntries];
dataSet.colors = @[[UIColor greenColor], [UIColor blueColor]];
dataSet.axisDependency = AxisDependencyLeft; // 根據左邊數據顯示
dataSet.drawValuesEnabled = NO; // 是否顯示數據
BarChartData *data = [[BarChartData alloc] initWithDataSets:@[dataSet]];
data.barWidth = 0.55f; // 柱狀圖寬度(數值范圍 0 ~ 1)
return data;
}
3. 設置折線圖屬性
下面將設置折線圖屬性與數據進行了封裝。
/**
獲取折線圖的數據
@param lineValues 數據放入數組 NSNumber 類型
@return 折線圖的數據
*/
- (LineChartData *)getLineData:(NSArray *)lineValues {
NSMutableArray *entries = [NSMutableArray array];
for (int i = 0; i < lineValues.count; i++) {
ChartDataEntry *entry = [[ChartDataEntry alloc] initWithX:i y:[lineValues[i] floatValue]];
[entries addObject:entry];
}
LineChartDataSet *dataSet = [[LineChartDataSet alloc] initWithValues:entries];
dataSet.colors = @[[UIColor orangeColor]]; // 線的顏色
dataSet.lineWidth = 0.5f; // 線寬
dataSet.circleRadius = 2.5f; // 圓點外圓半徑
dataSet.circleHoleRadius = 1.5f; // 圓點內圓半徑
dataSet.circleColors = @[[UIColor orangeColor]]; // 圓點外圓顏色
dataSet.circleHoleColor = [UIColor whiteColor]; // 圓點內圓顏色
dataSet.axisDependency = AxisDependencyRight; // 根據右邊數據顯示
dataSet.drawValuesEnabled = NO; // 是否顯示數據
dataSet.mode = LineChartModeCubicBezier; // 折線圖類型
dataSet.drawFilledEnabled = YES; // 是否顯示折線圖陰影
NSArray *shadowColors = @[(id)[[UIColor orangeColor] colorWithAlphaComponent:0].CGColor, (id)[[UIColor orangeColor] colorWithAlphaComponent:0.7].CGColor];
CGGradientRef gradient = CGGradientCreateWithColors(nil, (CFArrayRef)shadowColors, nil);
dataSet.fill = [ChartFill fillWithLinearGradient:gradient angle:90.0f]; // 陰影漸變效果
dataSet.fillAlpha = 1.0f; // 陰影透明度
LineChartData *lineData = [[LineChartData alloc] initWithDataSet:dataSet];
return lineData;
}
4. 設置圖表數據
最后通過以下方法就能夠設置整個圖表的數據。
/**
設置混合圖表的數據
@param xValues X 軸的數據
@param bar1Values 柱狀圖數據1
@param bar2Values 柱狀圖數據2
@param lineValues 折線圖數據
*/
- (void)setXValues:(NSArray *)xValues
bar1Values:(NSArray *)bar1Values
bar2Values:(NSArray *)bar2Values
lineValues:(NSArray *)lineValues {
CombinedChartData *data = [[CombinedChartData alloc] init];
data.barData = [self getBarData:bar1Values bar2Values:bar2Values]; // 柱狀圖數據
data.lineData = [self getLineData:lineValues]; // 折線圖數據
[_combinedChartView setData:data]; // 圖表數據
ChartXAxis *xAxis = _combinedChartView.xAxis;
xAxis.axisMinimum = data.xMin - 0.5f; // X 軸最小數量
xAxis.axisMaximum = data.xMax + 0.5f; // X 軸最大數量
xAxis.valueFormatter = [[ChartIndexAxisValueFormatter alloc] initWithValues:xValues]; // X 軸數據
[_combinedChartView setVisibleXRangeMaximum:7]; // X 軸最多顯示數量(其余可滑動顯示)
[_combinedChartView animateWithYAxisDuration:1.0]; // 添加 Y 軸動畫
[_combinedChartView notifyDataSetChanged]; // 通知數據改變
}
另外這里有一個坑,在設置 X
軸屬性的時候,有個屬性是 xAxis.labelWidth
,由于圖表好像是自動計算 label
大小的,設置這個屬性根本沒有任何作用,所以就導致當 X
軸數據文字過長時,就全部擠在一塊了,為了解決這個問題,可以在設置 xAxis.valueFormatter
的時候自定義一個 valueFormatter
來設定指定寬度截取,下面是我的自定義。
CustomAxisValueFormatter.h
@interface CustomAxisValueFormatter : NSObject <IChartAxisValueFormatter>
- (instancetype)initWithValues:(NSArray *)values labelWidth:(CGFloat)labelWidth;
@end
CustomAxisValueFormatter.m
@interface CustomAxisValueFormatter ()
@property (nonatomic, strong) NSArray *values;
@property (nonatomic, assign) CGFloat labelWidth;
@end
@implementation CustomAxisValueFormatter
- (instancetype)initWithValues:(NSArray *)values labelWidth:(CGFloat)labelWidth
{
self = [super init];
if (self) {
_values = values;
_labelWidth = labelWidth;
}
return self;
}
- (NSString *)stringForValue:(double)value axis:(ChartAxisBase *)axis {
/* 根據設置的字符寬度自動截取字符長度 */
NSString *result = @"";
NSInteger index = @(value).integerValue;
if (index < _values.count) {
result = _values[index];
if (_labelWidth > 0) {
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:10]; // 這里和 X 軸文字字號設置一樣大
label.text = result;
while ([label sizeThatFits:CGSizeMake(MAXFLOAT, MAXFLOAT)].width > _labelWidth) {
result = [result substringToIndex:result.length - 1];
label.text = [NSString stringWithFormat:@"%@...", result];
}
return label.text;
}
}
return result;
}
@end
這是之前設置 xAxis.valueFormatter
的方法。
xAxis.valueFormatter = [[ChartIndexAxisValueFormatter alloc] initWithValues:xValues];
改為自定義的方法。
xAxis.valueFormatter = [[CustomAxisValueFormatter alloc] initWithValues:xValues labelWidth:40];
5. 設置數據方法
圖表的數據全部放入數組中,數字用 NSNumber
類型。
[self setXValues:@[@"X軸數據1", @"X軸數據2", @"X軸數據3"] bar1Values:@[@"10", @"20", @"30"] bar2Values:@[@"30", @"20", @"10"] lineValues:@[@"40", @"50", @"60"]];
下面是我們項目做出來的顯示效果:
圖表顯示效果
其實
CombinedChartView
混合圖表只是將BarChartView
柱狀圖和LineChartView
折線圖合在一起了,設置還是分開設置的,所以不管是柱狀圖還是折線圖,都可以參照上面來設置。
將來的你,一定會感激現在拼命的自己,愿自己與讀者的開發之路無限美好。