您的位置:首页 > 移动开发 > IOS开发

iOS可复用控件之折线图

2017-10-13 14:27 204 查看
GitHub地址:https://github.com/runThor/HTChart

可支持左右拖动、双指放大缩小操作。

学习过PNChart的源码,这里是一个最基础、很简化的折线图。

效果:



实现:

折线类:

// HTLine.h

#import <UIKit/UIKit.h>

@interface HTLine : UIView

@property (nonatomic, strong) NSMutableArray *dataArr; // 此条折线的数据
@property (nonatomic, strong) UIColor *lineColor; // 此条折线的颜色

@end
//  HTLine.m

#import "HTLine.h"

@implementation HTLine

- (instancetype)init {
if (self = [super init]) {
self.dataArr = [[NSMutableArray alloc] init];
}

return self;
}

@end

图表类:

// HTChartView.h

#import <UIKit/UIKit.h>
#import "HTLine.h"

@interface HTChartView : UIView

@property (nonatomic, assign) CGFloat maxValue; // y轴的上限值
@property (nonatomic, assign) CGFloat minValue; // y轴的下限值

- (void)addLines:(NSArray *)lines; // 往图表中添加折线

@end

//  HTChartView.m

#import "HTChartView.h"

#define VIEW_WIDTH  self.frame.size.width
#define VIEW_HEIGHT self.frame.size.height
#define MAX_POINT_INTERVAL  60  // 左右相邻点最大间隔,控制放大极限
#define MIN_POINT_INTERVAL  5  // 左右相邻点最小间隔,控制缩小极限

@interface HTChartView ()

@property (nonatomic, assign) CGFloat topMargin;  // 与屏幕上边距
@property (nonatomic, assign) CGFloat bottomMargin;  // 与屏幕下边距
@property (nonatomic, assign) CGFloat leftMargin;  // 与屏幕左边距
@property (nonatomic, assign) CGFloat rightMargin;  // 与屏幕右边距
@property (nonatomic, assign) NSInteger horizontalValueLinesCount;  // 从y轴刻度处延伸出x轴水平线的数量,即y轴上的刻度个数
@property (nonatomic, assign) CGFloat contentOffset;  // 折线图的偏移量,正数,最小为0
@property (nonatomic, assign) CGFloat pointInterval;  // 左右相邻数据点的间隔
@property (nonatomic, strong) NSMutableArray *linesArr;  // 所有折线
@property (nonatomic, strong) NSMutableArray *dataOriginArr;  // 所有折线的数据点位置

@end

@implementation HTChartView

- (instancetype)initWithFrame:(CGRect)frame {

if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
// 设置图表的初始状态
self.topMargin = 20.0;
self.bottomMargin = 20.0;
self.leftMargin = 30.0;
self.rightMargin = 10.0;
self.horizontalValueLinesCount = 5;
self.pointInterval = 40;
self.linesArr = [[NSMutableArray alloc] init];
}

return self;
}

- (void)drawRect:(CGRect)rect {
// 绘制图表框架
[self drawChartFrame];
// 绘制折线
[self drawLines];
}

// 绘制图表框架
- (void)drawChartFrame {
CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);

// 画y轴
CGContextMoveToPoint(context, self.leftMargin, self.topMargin);
CGContextAddLineToPoint(context, self.leftMargin, VIEW_HEIGHT - self.bottomMargin);

// 画x轴
CGContextAddLineToPoint(context, VIEW_WIDTH - self.rightMargin, VIEW_HEIGHT - self.bottomMargin);

// 画右边框
CGContextAddLineToPoint(context, VIEW_WIDTH - self.rightMargin, self.topMargin);
// 绘制
CGContextStrokePath(context);

// 绘制x轴的水平线
CGContextSetRGBStrokeColor(context, 1, 1, 1, 0.3);

CGFloat eachHeight = (VIEW_HEIGHT - self.topMargin - self.bottomMargin)/(self.horizontalValueLinesCount + 1);

for (int i = 1 ; i <= self.horizontalValueLinesCount; i++) {

CGContextMoveToPoint(context, self.leftMargin, self.topMargin + eachHeight * i);
CGContextAddLineToPoint(context, VIEW_WIDTH - self.rightMargin, self.topMargin + eachHeight * i);

}

CGContextStrokePath(context);
}

// 绘制折线
- (void)drawLines {
CGContextRef context = UIGraphicsGetCurrentContext();

for (int lineIndex = 0; lineIndex < self.linesArr.count; lineIndex++) {
HTLine *line = self.linesArr[lineIndex];

// 计算数据点位置
NSMutableArray *originArr = [[NSMutableArray alloc] init];

for (int dataIndex = 0; dataIndex < line.dataArr.count; dataIndex++) {
CGFloat x = self.leftMargin + dataIndex * self.point
a936
Interval - self.contentOffset;
CGFloat y = VIEW_HEIGHT - ([line.dataArr[dataIndex] floatValue]/(self.maxValue - self.minValue) * (VIEW_HEIGHT - self.topMargin - self.bottomMargin) + self.bottomMargin);

[originArr addObject:NSStringFromCGPoint(CGPointMake(x, y))];
}

[self.dataOriginArr addObject:originArr];

// 绘制数据点
CGContextSetFillColorWithColor(context, line.lineColor.CGColor);

for (NSString *origin in originArr) {
CGPoint dataOrigin = CGPointFromString(origin);

if (dataOrigin.x >= self.leftMargin) {
if (dataOrigin.x > VIEW_WIDTH - self.rightMargin) {
break;
} else {
CGContextFillEllipseInRect(context, CGRectMake(dataOrigin.x - 2.5, dataOrigin.y - 2.5, 5, 5));
}
}
}

// 各数据点连成折线
CGContextSetStrokeColorWithColor(context, line.lineColor.CGColor);

BOOL startDrawing = NO;

for (int dataIndex = 0; dataIndex < originArr.count; dataIndex++) {
CGPoint dataOrigin = CGPointFromString(originArr[dataIndex]);

if (startDrawing == NO) {
if (dataOrigin.x >= self.leftMargin) {
if (dataIndex == 0) {
CGContextMoveToPoint(context, dataOrigin.x, dataOrigin.y);
} else {
// 与y轴的交点
CGPoint lastDataOrigin = CGPointFromString(originArr[dataIndex - 1]);
CGFloat startPointY = dataOrigin.y - (dataOrigin.x - self.leftMargin)/self.pointInterval * (dataOrigin.y - lastDataOrigin.y);

CGContextMoveToPoint(context, self.leftMargin, startPointY);
CGContextAddLineToPoint(context, dataOrigin.x, dataOrigin.y);
}

startDrawing = YES;
}
} else {
if (dataOrigin.x >= VIEW_WIDTH - self.rightMargin) {
// 与右边框的交点
CGPoint lastDataOrigin = CGPointFromString(originArr[dataIndex - 1]);
CGFloat endPointY = dataOrigin.y - (dataOrigin.x - (VIEW_WIDTH - self.rightMargin))/self.pointInterval * (dataOrigin.y - lastDataOrigin.y);

CGContextAddLineToPoint(context, VIEW_WIDTH - self.rightMargin, endPointY);

break;
} else {
CGContextAddLineToPoint(context, dataOrigin.x, dataOrigin.y);
}
}
}

CGContextStrokePath(context);
}
}

// 往图表中添加折线
- (void)addLines:(NSArray *)lines {
[self.linesArr addObjectsFromArray:lines];

[self setNeedsDisplay];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CGFloat lineLength = self.pointInterval * ([self.linesArr[0] dataArr].count - 1);

NSArray *touchesArr = [event allTouches].allObjects;
// 单指拖动时,改变折线图偏移量
if (1 == touchesArr.count) {
CGPoint currentTouchPoint = [[touches anyObject] locationInView:self];
CGPoint previousTouchPoint = [[touches anyObject] previousLocationInView:self];

self.contentOffset += previousTouchPoint.x - currentTouchPoint.x;
} else if (2 == touchesArr.count) {
// 双指缩放时,改变图表偏移量及相邻点间隔
CGPoint currentOnePoint = [touchesArr[0] locationInView:self];
CGPoint currentAnotherPoint = [touchesArr[1] locationInView:self];
CGPoint previousOnePoint = [touchesArr[0] previousLocationInView:self];
CGPoint previousAnotherPoint = [touchesArr[1] previousLocationInView:self];

CGFloat currentFingerSpacing = fabs(currentOnePoint.x - currentAnotherPoint.x);
CGFloat previousFingerSpacing = fabs(previousOnePoint.x - previousAnotherPoint.x);
CGFloat centerX = (currentOnePoint.x - currentAnotherPoint.x)/2 + currentAnotherPoint.x;

if (currentFingerSpacing > previousFingerSpacing && self.pointInterval < MAX_POINT_INTERVAL) {
// 放大
self.pointInterval *= 1.05;
self.contentOffset = self.contentOffset * 1.05 + (centerX - self.leftMargin) * 0.05;
} else if (currentFingerSpacing < previousFingerSpacing && self.pointInterval > MIN_POINT_INTERVAL) {
// 缩小
self.pointInterval *= 0.95;
self.contentOffset = self.contentOffset * 0.95 - (centerX - self.leftMargin) * 0.05;
}
}

// 控制折线图偏移极限
if (self.contentOffset < 0) {
self.contentOffset = 0;
} else if (self.contentOffset > lineLength) {
self.contentOffset = lineLength;
}

// 刷新折线图
[self setNeedsDisplay];
}


使用:
// ViewController.m

#import "ViewController.h"
#import "HTChartView.h"

@interface ViewController ()

@property (nonatomic, strong)HTChartView *chartView;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

// 初始化图表
self.chartView = [[HTChartView alloc] initWithFrame:CGRectMake(0, 0, 320, 250)];
self.chartView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
[self.chartView setCenter:CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/2)];
self.chartView.maxValue = 10;
self.chartView.minValue = 0;
[self.view addSubview:self.chartView];

// 添加折线
[self addLines];
}

// 模拟折线数据
- (void)addLines {
HTLine *yellowLine = [[HTLine alloc] init];
[yellowLine.dataArr addObjectsFromArray:@[@(1), @(3), @(5), @(7), @(9), @(5), @(6), @(4), @(2), @(8), @(1), @(6), @(4), @(5), @(9), @(8), @(2)]];
yellowLine.lineColor = [UIColor yellowColor];

HTLine *redLine = [[HTLine alloc] init];
[redLine.dataArr addObjectsFromArray:@[@(2), @(4), @(5), @(8), @(6), @(1), @(7), @(5), @(3), @(4), @(6), @(2), @(8), @(7), @(9), @(5), @(2)]];
redLine.lineColor = [UIColor redColor];

NSArray *linesArr = @[yellowLine, redLine];
[self.chartView addLines:linesArr];
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: