【iOS 开发】Objective-C 中 Charts 的 CombinedChart 使用汇总

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 折线图合在一起了,设置还是分开设置的,所以不管是柱状图还是折线图,都可以参照上面来设置。

将来的你,一定会感激现在拼命的自己,愿自己与读者的开发之路无限美好。

我的传送门: 博客简书微博GitHub

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容