最近公司项目中要做一个报表功能,需要用到图表,于是就使用了 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
折线图合在一起了,设置还是分开设置的,所以不管是柱状图还是折线图,都可以参照上面来设置。
将来的你,一定会感激现在拼命的自己,愿自己与读者的开发之路无限美好。