实现一个简单的滑动的 line chart

最近项目上有一个折线图的需求,根据实际的产品需求、面向服务器接口数据手撸了一个,自己写随意玩=。=

PXLineChart

  • 支持y轴数据不等分


    abc.gif

Demo地址:
OC版本PXLineChart_OC
Swift版本PXLineChart_Swift

使用

为了最大化的可定制,基于UITableView delegate设计模式来提供对外接口

#import "ViewController.h"
#import "PXLineChartView.h"
#import "PointItem.h"

@interface ViewController ()<PXLineChartViewDelegate>

@property (nonatomic, weak) IBOutlet PXLineChartView *pXLineChartView;
@property (nonatomic, strong) NSArray *lines;//line count
@property (nonatomic, strong) NSArray *xElements;//x轴数据
@property (nonatomic, strong) NSArray *yElements;//y轴数据
@end

#pragma mark -

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _pXLineChartView.delegate = self;
    _xElements = @[@"16-2",@"16-3",@"16-4",@"16-5",@"16-6",@"16-7",@"16-8",@"16-9",@"16-10",@"16-11",@"16-12",@"17-01",@"17-02",@"17-03",@"17-04",@"17-05"];
    _yElements = @[@"1000",@"2000",@"3000",@"4000",@"5000"];
    
    self.lines = [self lines:NO];
    // Do any additional setup after loading the view, typically from a nib.
}

- (NSArray *)lines:(BOOL)fill {
    NSArray *pointsArr = @[@{@"xValue" : @"16-2", @"yValue" : @"1000"},
                           @{@"xValue" : @"16-4", @"yValue" : @"2000"},
                           @{@"xValue" : @"16-6", @"yValue" : @"1700"},
                           @{@"xValue" : @"16-8", @"yValue" : @"3100"},
                           @{@"xValue" : @"16-9", @"yValue" : @"3500"},
                           @{@"xValue" : @"16-12", @"yValue" : @"3400"},
                           @{@"xValue" : @"17-02", @"yValue" : @"1100"},
                           @{@"xValue" : @"17-04", @"yValue" : @"1500"}];
    
    NSArray *pointsArr1 = @[@{@"xValue" : @"16-2", @"yValue" : @"2000"},
                            @{@"xValue" : @"16-3", @"yValue" : @"2200"},
                            @{@"xValue" : @"16-4", @"yValue" : @"3000"},
                            @{@"xValue" : @"16-6", @"yValue" : @"3750"},
                            @{@"xValue" : @"16-7", @"yValue" : @"3800"},
                            @{@"xValue" : @"16-8", @"yValue" : @"4000"},
                            @{@"xValue" : @"16-10", @"yValue" : @"2000"}];
    
    NSMutableArray *points = @[].mutableCopy;
    for (int i = 0; i < pointsArr.count; i++) {
        PointItem *item = [[PointItem alloc] init];
        NSDictionary *itemDic = pointsArr[i];
        item.price = itemDic[@"yValue"];
        item.time = itemDic[@"xValue"];
        item.chartLineColor = [UIColor redColor];
        item.chartPointColor = [UIColor redColor];
        item.pointValueColor = [UIColor redColor];
        if (fill) {
            item.chartFillColor = [UIColor colorWithRed:0 green:0.5 blue:0.2 alpha:0.5];
            item.chartFill = YES;
        }
        [points addObject:item];
    }
    
    NSMutableArray *pointss = @[].mutableCopy;
    for (int i = 0; i < pointsArr1.count; i++) {
        PointItem *item = [[PointItem alloc] init];
        NSDictionary *itemDic = pointsArr1[i];
        item.price = itemDic[@"yValue"];
        item.time = itemDic[@"xValue"];
        item.chartLineColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        item.chartPointColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        item.pointValueColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        if (fill) {
            item.chartFillColor = [UIColor colorWithRed:0.5 green:0.1 blue:0.8 alpha:0.5];
            item.chartFill = YES;
        }
        [pointss addObject:item];
    }
    //两条line
    return @[pointss,points];
}

#pragma mark PXLineChartViewDelegate
//通用设置
- (NSDictionary<NSString*, NSString*> *)lineChartViewAxisAttributes {
    return @{yElementInterval : @"40",
             xElementInterval : @"40",
             yMargin : @"50",
             xMargin : @"25",
             yAxisColor : [UIColor colorWithRed:200.0/255 green:200.0/255 blue:200.0/255 alpha:1],
             xAxisColor : [UIColor colorWithRed:200.0/255 green:200.0/255 blue:200.0/255 alpha:1],
             gridColor : [UIColor colorWithRed:244.0/255 green:244.0/255 blue:244.0/255 alpha:1],
             gridHide : @0,
             pointHide : @0,
             pointFont : [UIFont systemFontOfSize:10]};
}
//line count
- (NSUInteger)numberOfChartlines {
    return self.lines.count;
}
//x轴y轴对应的元素count
- (NSUInteger)numberOfElementsCountWithAxisType:(AxisType)axisType {
    return (axisType == AxisTypeY)? _yElements.count : _xElements.count;
}
//x轴y轴对应的元素view
- (UILabel *)elementWithAxisType:(AxisType)axisType index:(NSUInteger)index {
    UILabel *label = [[UILabel alloc] init];
    NSString *axisValue = @"";
    if (axisType == AxisTypeX) {
        axisValue = _xElements[index];
        label.textAlignment = NSTextAlignmentCenter;//;
    }else if(axisType == AxisTypeY){
        axisValue = _yElements[index];
        label.textAlignment = NSTextAlignmentRight;//;
    }
    label.text = axisValue;
    label.font = [UIFont systemFontOfSize:12];
    label.textColor = [UIColor blackColor];
    return label;
}
//每条line对应的point数组
- (NSArray<id<PointItemProtocol>> *)plotsOflineIndex:(NSUInteger)lineIndex {
    return self.lines[lineIndex];
}
//点击point回调响应
- (void)elementDidClickedWithPointSuperIndex:(NSUInteger)superidnex pointSubIndex:(NSUInteger)subindex {
    PointItem *item = self.lines[superidnex][subindex];
    NSString *xTitle = item.time;
    NSString *yTitle = item.price;
    UIAlertController *alertView = [UIAlertController alertControllerWithTitle:yTitle
                                                                       message:[NSString stringWithFormat:@"x:%@ \ny:%@",xTitle,yTitle] preferredStyle:UIAlertControllerStyleAlert];
    [alertView addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }]];
    [self presentViewController:alertView animated:YES completion:nil];
}

static bool fill = NO;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    fill = !fill;
    self.lines = [self lines:fill];
    [_pXLineChartView reloadData];
}

数据源三组:X轴、Y轴、真正的折线数组lines
X\Y轴对应的NSString数组,lines对应的二维数组
着重看下lines的数据结构

NSArray *pointsArr = @[@{@"xValue" : @"16-2", @"yValue" : @"1000"},
                           @{@"xValue" : @"16-4", @"yValue" : @"2000"},
                           @{@"xValue" : @"16-6", @"yValue" : @"1700"},
                           @{@"xValue" : @"16-8", @"yValue" : @"3100"},
                           @{@"xValue" : @"16-9", @"yValue" : @"3500"},
                           @{@"xValue" : @"16-12", @"yValue" : @"3400"},
                           @{@"xValue" : @"17-02", @"yValue" : @"1100"},
                           @{@"xValue" : @"17-04", @"yValue" : @"1500"}];
    
    NSArray *pointsArr1 = @[@{@"xValue" : @"16-2", @"yValue" : @"2000"},
                            @{@"xValue" : @"16-3", @"yValue" : @"2200"},
                            @{@"xValue" : @"16-4", @"yValue" : @"3000"},
                            @{@"xValue" : @"16-6", @"yValue" : @"3750"},
                            @{@"xValue" : @"16-7", @"yValue" : @"3800"},
                            @{@"xValue" : @"16-8", @"yValue" : @"4000"},
                            @{@"xValue" : @"16-10", @"yValue" : @"2000"}];
    
    NSMutableArray *points = @[].mutableCopy;
    for (int i = 0; i < pointsArr.count; i++) {
        PointItem *item = [[PointItem alloc] init];
        NSDictionary *itemDic = pointsArr[i];
        item.price = itemDic[@"yValue"];
        item.time = itemDic[@"xValue"];
        item.chartLineColor = [UIColor redColor];
        item.chartPointColor = [UIColor redColor];
        item.pointValueColor = [UIColor redColor];
        if (fill) {
            item.chartFillColor = [UIColor colorWithRed:0 green:0.5 blue:0.2 alpha:0.5];
            item.chartFill = YES;
        }
        [points addObject:item];
    }
    
    NSMutableArray *pointss = @[].mutableCopy;
    for (int i = 0; i < pointsArr1.count; i++) {
        PointItem *item = [[PointItem alloc] init];
        NSDictionary *itemDic = pointsArr1[i];
        item.price = itemDic[@"yValue"];
        item.time = itemDic[@"xValue"];
        item.chartLineColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        item.chartPointColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        item.pointValueColor = [UIColor colorWithRed:0.2 green:1 blue:0.7 alpha:1];
        if (fill) {
            item.chartFillColor = [UIColor colorWithRed:0.5 green:0.1 blue:0.8 alpha:0.5];
            item.chartFill = YES;
        }
        [pointss addObject:item];
    }
    //两条line
    return @[pointss,points];

lines数组里面装了两个子数组pointss、points,pointss\ points数组里面装的是我们自定义的model,此model必须实现协议PointItemProtocol
PointItemProtocol.h

@protocol PointItemProtocol <NSObject>

@required
- (NSString *)px_pointYvalue; //y坐标值
- (NSString *)px_pointXvalue; //x坐标值

@optional

- (UIColor *)px_chartLineColor;//折线color
- (UIColor *)px_chartPointColor;//point color
- (UIColor *)px_pointValueColor;//
- (CGSize)px_pointSize;//point size
- (UIColor *)px_chartFillColor;////fill color - UIColor
- (BOOL)px_chartFill;//是否fill - NSNumber(@1-fill; @0-不fill)

自定义model必须实现- (NSString *)px_pointYvalue;- (NSString *)px_pointXvalue; 分别对应的y、x值

@implementation PointItem

#pragma PointItemProtocol

- (NSString *)px_pointYvalue {
    return _price;
}
- (NSString *)px_pointXvalue {
    return _time;
}
- (UIColor *)px_chartLineColor {
    return _chartLineColor;
}

- (UIColor *)px_chartPointColor {
    return _chartPointColor;
}

- (UIColor *)px_pointValueColor {
    return _pointValueColor;
}

- (UIColor *)px_chartFillColor {
    return _chartFillColor;
}

- (BOOL)px_chartFill {
    return _chartFill;
}

也就是对每条line的差异化配置都在里面,这个后续可以根据所需进行扩展;
通用化的配置在PXLineChartViewDelegate:

#pragma mark PXLineChartViewDelegate
//通用设置
- (NSDictionary<NSString*, NSString*> *)lineChartViewAxisAttributes {
    return @{yElementInterval : @"40",
             xElementInterval : @"40",
             yMargin : @"50",
             xMargin : @"25",
             yAxisColor : [UIColor colorWithRed:200.0/255 green:200.0/255 blue:200.0/255 alpha:1],
             xAxisColor : [UIColor colorWithRed:200.0/255 green:200.0/255 blue:200.0/255 alpha:1],
             gridColor : [UIColor colorWithRed:244.0/255 green:244.0/255 blue:244.0/255 alpha:1],
             gridHide : @0,
             pointHide : @0,
             pointFont : [UIFont systemFontOfSize:10]};
}

所有通用化配置参数参见PXLineChartConst.h;后续可在里面进行自定义扩展
我们看下其他delegate 方法

//line count
- (NSUInteger)numberOfChartlines {
    return self.lines.count;
}

返回折线个数

//x轴y轴对应的元素count
- (NSUInteger)numberOfElementsCountWithAxisType:(AxisType)axisType {
    return (axisType == AxisTypeY)? _yElements.count : _xElements.count;
}
//x轴y轴对应的元素view
- (UILabel *)elementWithAxisType:(AxisType)axisType index:(NSUInteger)index {
    UILabel *label = [[UILabel alloc] init];
    NSString *axisValue = @"";
    if (axisType == AxisTypeX) {
        axisValue = _xElements[index];
        label.textAlignment = NSTextAlignmentCenter;//;
    }else if(axisType == AxisTypeY){
        axisValue = _yElements[index];
        label.textAlignment = NSTextAlignmentRight;//;
    }
    label.text = axisValue;
    label.font = [UIFont systemFontOfSize:12];
    label.textColor = [UIColor blackColor];
    return label;
}

对X\Y轴元素视图定制

//每条line对应的point数组
- (NSArray<id<PointItemProtocol>> *)plotsOflineIndex:(NSUInteger)lineIndex {
    return self.lines[lineIndex];
}

返回每条折线对应的点

以上是PXLineChartViewDelegate必须实现的方法

//点击point回调响应
- (void)elementDidClickedWithPointSuperIndex:(NSUInteger)superidnex pointSubIndex:(NSUInteger)subindex {
    PointItem *item = self.lines[superidnex][subindex];
    NSString *xTitle = item.time;
    NSString *yTitle = item.price;
    UIAlertController *alertView = [UIAlertController alertControllerWithTitle:yTitle
                                                                       message:[NSString stringWithFormat:@"x:%@ \ny:%@",xTitle,yTitle] preferredStyle:UIAlertControllerStyleAlert];
    [alertView addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
    }]];
    [self presentViewController:alertView animated:YES completion:nil];
}

点击回调可选

我是分割线,但是没有线

如果我们想填充可以在设置PointItem的chartFill和chartFillColor属性

self.lines = [self lines:YES];
[_pXLineChartView reloadData];

类似UITableView我们可以对其刷新

[_pXLineChartView reloadData];

以上具体使用差不多说完了,实际开发中和服务器童鞋对接接口的时候返回的json model实现PointItemProtocol给x、y填值,具体参考demo,另外核心画线操作在PXChartBackgroundView类里,具体下载查看
Demo地址:
OC版本PXLineChart_OC
Swift版本PXLineChart_Swift
如果觉得还有用,欢迎star鼓励,我会不断扩展优化迭代。

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

推荐阅读更多精彩内容